☰
云打印机 MQTT 直连实战:完整字段表、JSON / XML 排版与防重打设计
2026/10/24 2:10:00 网站建设 项目流程

云打印机 MQTT 直连实战:完整字段表、JSON / XML 排版与防重打设计(附 Python 示例)

大多数商用云打印机只有「设备 → 厂商云 → 你的系统」一条路,数据必须经过厂商。本文给出一条可私有化的路径:设备直连你自有的 MQTT Broker,打印内容全程不落厂商云。含官方通信协议(v2025-11-10)的完整下行字段表、XML 与 JSON 两套排版示例、14 项上报状态码、id防重打设计,以及 Broker 选型与接入要点。


一、为什么需要「直连」

市面上商用云打印机的默认架构是云中转:

打印机 ←→ 厂商云 ←→ 你的 SaaS / ERP

这条链路的问题不在功能,在合规与自主权:

  • 金融 / 医疗 / 政务客户的打印内容(单据、处方、凭条)不得落到第三方服务器;
  • 你的业务系统里,设备是厂商的资产—— 换供应商等于换掉全部设备;
  • 厂商云故障 = 你的全部门店停摆。

MQTT 直连把中间那一跳去掉:

打印机 ←→ 你自有的 MQTT Broker

设备只认 Broker 地址与凭证,打印内容不出你的服务器。协议规范(MQTT 直连 / HTTP / 自定义指令)计划于 2026-10 以 Apache 2.0 开放,届时你可以自行实现 Broker 端,也可以在多家供应商之间迁移而不必换设备。

云中转与直连不是二选一 —— 成熟的方案两套都支持,按客户合规等级切换。该直连方案目前已有国内与海外用户商用运行。

二、协议全貌:三条约定 + 一种配置方式

官方《云打印机对接第三方 MQTT 服务器通信协议》只约束三件事,其余交给部署方:

项约定
可接入的服务器类型阿里云 MQTT、腾讯云 MQTT、自建 MQTT、电信云等(有定制需求可另行对接)
QoS订阅主题与发布主题一律 QoS = 1
Clean Session设备默认清除会话;若希望设备接收离线期间的消息,需在 PC 配置工具写入参数时选择「不清除 Session」
参数写入方式设备开机后通过USB 数据线连接 PC,用《云打印设备配置工具》写入 Broker 地址与凭证

设备侧覆盖热敏打印机、标签打印机、打印云盒三类形态。

这里有一个容易误解的点:协议不定义「设备如何知道自己该连哪个 Broker」—— 因为它不是协议行为,而是出厂 / 现场配置行为:参数经 USB 写入设备。所以部署流程里必然有一步「批量写入参数」,这步用官方 PC 工具即可完成,不需要自己改固件。

三、Topic 主题:规范约定「方向」,不固化「字符串」

协议只约定主题的角色,不约定具体字符串:

  • 订阅主题:接收打印任务、语音播报消息、命令消息 —— QoS = 1
  • 发布主题:上报设备状态与打印结果 —— QoS = 1

也就是说sw/xxx/task/down这类字符串不属于协议定义范围,而由部署方自定义。下面给一套推荐命名方案(工程建议,非协议强制),好处是便于通配符订阅与分环境隔离:

推荐 Topic方向QoS用途
{product}/{ver}/{sn}/task下行1打印任务
{product}/{ver}/{sn}/voice下行1语音播报
{product}/{ver}/{sn}/cmd下行1配置管理命令
{product}/{ver}/{sn}/status上行1设备状态与打印结果

平台侧用+通配符订阅,避免为每台设备单独建订阅:

{product}/{ver}/+/status

落地时请把主题字符串写进你的部署文档 —— 因为协议不强制,跨供应商迁移时这部分要重新约定,这是直连方案需要提前规划的一点。

四、下行:打印与语音消息字段表

服务器推送到设备的消息为 UTF-8 JSON。完整字段如下(官方 v2025-11-10 规范):

关键字类型必填说明
iduint32是打印任务 ID;服务端确保不重复,用于防重复打印。设备上报结果以此 ID 标识任务。取值范围0 – 4294967295
typeint是打印数据排版类型(打印语言),见下表
contentsString / Array / Object是打印内容,数据格式与type保持一致
pWidthint否打印纸张宽度 mm:58 / 80 / 110,默认 58
pCopyint否打印份数,默认 1
pTypeint否纸张类型,默认 1;纸张为标签时必传
vTypeint否播报类型
vMessagestring否语音播报文本(TTS 合成)或音频下载 URL
vModeint否播报排队策略,默认 0
messagestring否预留 HTTP 下载类型的打印内容;与contents二选一,优先取contents
gatewaystring否仅打印云盒:"usb"发往 USB 打印机;"192.168.2.11"发往指定局域网 IP 打印机

type取值

值含义contents形态
1ESC(BASE64 编码后数据)字符串
2CPCL字符串
3TSPL字符串
4XML(UTF-8)字符串
5JSON(UTF-8)Array(小票)/ Object(标签)
6PNG(URL 链接或 base64 内容)字符串
99打印设备配置管理Object

其中type=4的 XML 又分两种设计:基础 XML兼容市面各运打印平台;矢量打印 XML是专为全球语言打印设计的排版框架。XML 与 JSON 的完整排版定义见官网文档中心:https://www.sw-aiot.com/doc.html。

pType取值:1 连续纸(默认)|2 标签纸|3 黑标纸|4 穿孔纸|8 连续纸(用标签指令打印小票)

vType取值:-1 无播报|0 定制语音(配合vMessage)|1 播报「你有新的订单」|10000 指定语音链接下载播报

vMode取值:0 按队列顺序播报,任务先插入队列(默认)|1 立即播报|2 立即播报并清空待播报队列

五、下行示例(均取自官方规范)

5.1 XML 排版(type=4)—— 58mm 小票:

{"id":12345678,"type":4,"pType":1,"pWidth":58,"pCopy":1,"vType":0,"vMessage":"测试语音","contents":"<CB>收款小票</CB><BR>商户存根 请妥善保管<BR>--------------------------------<BR>商户名称 小熊每日鲜水果店<BR>商户编号 1005800029<BR>交易时间 2020-12-11 10:23:30<BR>交易订单号 1005800012112007217<BR>支付单号 202012112200140385141256912<BR>优惠金额 5.00<BR>备注 测试小票<BR>交易状态 收款成功<BR>支付方式 支付宝-公众号支付<BR>交易金额 126.50 元<BR><BR><QR>10058001200047217</QR><BR>--------------------------------<BR>打印时间 2020-11-12 11:02:36 签名<BR>"}

常用 XML 标签:<BR>换行|<CB>居中放大|<B>放大一倍|<C>居中|<L>字体变高一倍|<W>字体变宽一倍|<BOLD>加粗|<RIGHT>右对齐|<CUT>切纸|<PLUGIN>钱箱或外置音响|<IMG>PNG 图片 URL|<QR>二维码(单个订单最多打印一个)|<BC128_A>/<BC128>/<BC128_C>条码(A 为数字 + 大写字母混合,58mm 最多 14 位;C 为纯数字)

5.2 JSON 排版 · 小票(type=5,contents为 Array):

{"id":12345678,"vType":0,"vMessage":"这是测试语音","pType":1,"pWidth":58,"pCopy":1,"type":5,"contents":[{"cont":"云打印 JSON 排版","type":"title"},{"thead":{"名称":"40%","单价":"25%","数量":"15%","金额":"20%"},"tbody":[["云打印机(4G/WiFi)",100,1,100],["4G 音箱(摄像头)",999.44,1,999.44]]},{"cont":"合计:2009.1 元","size":"11","align":"left"},{"both_sides":["收款金额","258.00"]},{"both_sides":["支付方式","微信支付"]},{"cont":"1234567890","type":"qrcode","align":"center","size":"60"},{"cont":"1","type":"cut"}]}

小票元素type:title(居中放大标题)|text(文本行,cont可为字符串或数组)|png(图片最大 60K)|bmp(图片最大 6K)|qrcode(最大 512 字节)|bc128(默认 CODE128A)/bc128a/bc128c|div_line(-分割线,支持居中插字)|div_star(*分割线)|cut(cont为"0"全切 /"1"半切)|plugin(开钱箱)

通用属性:align(left 默认 / center / right)|bold(true / false)|size(两位数字"AB",A 为倍宽、B 为倍高,各取 0–7)|both_sides(第一个字符串左对齐、第二个右对齐)|thead+tbody(多列表格,thead可传对象映射列宽占比,也可传数组只给占比)

5.3 JSON 排版 · 标签(type=5,contents为 Object):

{"id":12345678,"pType":2,"type":5,"contents":{"SIZE":[60,40],"DIRECTION":0,"DENSITY":7,"SOUND":3,"label":[{"type":"TEXT","x":10,"y":20,"font":"2","w":2,"h":2,"r":0,"cont":"放大 1 倍"},{"type":"QRCODE","x":10,"y":70,"e":"M","w":5,"cont":"12345678"},{"type":"BC128","x":240,"y":70,"h":30,"show":0,"r":0,"narrow":1,"wide":1,"cont":"1234567889"},{"type":"BAR","x":10,"y":300,"w":420,"h":4},{"type":"BITMAP","x":10,"y":200,"cont":"data:image/bmp;base64,..."}]}}

标签顶层参数:SIZE([宽, 高] mm)|DIRECTION|DENSITY|SOUND|label(元素数组)。
标签元素类型:TEXT(x/y/font/w/h/r/cont)|QRCODE(e纠错等级、w尺寸)|BC128(h高度、show是否显示可读字符、narrow/wide条宽比)|BAR(w/h画线)|BITMAP(base64 BMP)

同一份 JSON 载荷在HTTP 推送与MQTT 直连两条通路上完全通用 —— 你可以先用 HTTP 跑通原型,量上来后切 MQTT,业务侧序列化代码一行不用改。

六、下行:远程配置管理(type=99)

设备支持云端远程设置音量、重启、切换默认指令模式:

{"id":12345678,"type":99,"contents":{"cmd":"volume","value":1}}
命令载荷说明
volume{"cmd":"volume","value":1}音量:0 静音 / 1 低音量 / 2 中音量 / 3 大音量 / 4 最大
reboot{"cmd":"reboot"}系统重启
label_cmd{"cmd":"label_cmd","print":"tspl"}默认标签指令,可选tspl/cpcl/zpl,默认 tspl
print_cmd{"cmd":"print_cmd","cmd":"tspl"}设置打印机默认语言,可选tspl/cpcl/esc

七、上行:状态与打印结果上报

设备通过发布主题上报,UTF-8 JSON:

关键字类型说明
devicenamestring云打印设备 SN(机器码),与三元组的 DeviceName 或 ClientID 一致
iduint32打印任务 ID,与服务器下发的任务 ID 一致;上报设备状态时不带此字段,注意区分
codeint结果码

code状态码

code含义
0打印成功 / 打印机正常
100未知错误,通讯异常
101打印机缺纸
102打印机开盖
103打印机过热
201没有id
202没有type
203无效type参数(未在定义范围)
204没有 msg
205无效 msg(如链接非标准 HTTP)
206下载错误(重复下载 6 次)
207任务长度超出设备缓冲区
208内容格式错误
209重复任务,不执行打印(根据 ID 判断)

三类上报样例

// 4G 机型开机连上 MQTT 服务后上报一次(imei 为 15 位数字,iccid 为 20 位卡号){"devicename":"dev1","imei":"123456789012345","iccid":"12345678901234567890"}// 设备状态上报:缺纸(不带 id){"devicename":"dev1","code":101}// 打印结果上报:任务 123456789 打印成功{"devicename":"dev1","id":123456789,"code":0}

带扫码功能的产品另有一路上报:{"devicename":"dev1","scan_content":"abcd1234"}。

八、防重打:设备是最后一道闸

自建链路最容易踩的坑是同一张单子打两遍。这套协议把防线放在两层:

  1. 服务端:id由服务端保证不重复,下发前登记,落库唯一索引。
  2. 设备端:内置防重打 ——同一id在不断电情况下只打印一次,重复推送时直接回报code = 209(重复任务,不执行打印)。
# 服务端:下发前登记,落库唯一索引(列名对应协议里的 id)# CREATE UNIQUE INDEX uk_mqtt_id ON print_tasks(mqtt_id);# 设备端:命中已执行表 → 不打印,回报 {"devicename":"...","id":<id>,"code":209}

配套工程约定:

  • 设备只在打印完成后上报结果,回报体里的id与下发一致;
  • 服务端收到该id且code = 0才把任务置为「已完成」;
  • 未收到回报的任务超时后重新入队并复用同一个id(不要新建 ID)—— 即使重复到达,设备侧防重打会自动跳过。

⚠️id是uint32,上限 4294967295。若沿用雪花 ID 之类的 64 位方案,务必先截断或做映射,否则溢出后防重打逻辑会失效。

九、鉴权与部署要点

等级方案适用
L1 基础Username = 设备 Token,Password 可空内网 / 测试
L2 增强Password =HMAC-SHA256(device_secret, "{ts}\n{nonce}\n{device_sn}")公网生产
L3 强安全mTLS 客户端证书,CN={device_sn}, O={customer_id}金融 / 医疗 / 政企

生产环境必须 TLS(8883),这条没有妥协余地。其余建议值:

  • 端口:1883(TCP,仅限内网)/ 8883(TLS,生产必须);
  • Keep Alive 60s;
  • ClientID 建议使用设备 SN —— 规范要求devicename与三元组的 DeviceName 或 ClientID 一致,用随机 ClientID 会与设备侧会话互相踢下线;
  • 单条载荷做上限控制:JSON 票据里png≤ 60K、bmp≤ 6K、qrcode≤ 512 字节,超限会命中code = 207(超缓冲区)或208(格式错误)。大图请走type=6传 URL。

最小可运行示例(Python Paho)

importjsonimportpaho.mqtt.clientasmqtt BROKER,PORT="broker.example.com",8883DEVICE_SN="SW-03DW-ABC123"TOPIC_TASK=f"swprint/v1/{DEVICE_SN}/task"# 下行:设备订阅TOPIC_STATUS=f"swprint/v1/{DEVICE_SN}/status"# 上行:设备发布client=mqtt.Client(client_id=DEVICE_SN)client.username_pw_set("sw_dev_abc123def456","")client.tls_set(ca_certs="/path/to/ca.crt")defon_connect(c,userdata,flags,rc):ifrc!=0:returnc.subscribe(TOPIC_TASK,qos=1)# 接收打印任务 / 语音 / 命令task={"id":100001,"type":5,"pType":1,"pWidth":58,"pCopy":1,"contents":[{"cont":"深圳商为","type":"title"},{"cont":"Hello PB-03DW","type":"text","bold":True},{"cont":"https://sw-aiot.com","type":"qrcode","align":"center","size":"04"},],}c.publish(TOPIC_TASK,json.dumps(task,ensure_ascii=False),qos=1)defon_message(c,userdata,msg):# 设备侧上报:{"devicename":"...","id":100001,"code":0}print(msg.topic,msg.payload.decode("utf-8"))client.on_connect=on_connect client.on_message=on_message client.connect(BROKER,PORT,keepalive=60)client.loop_forever()

Node.js 侧同理(mqtt包,mqtts://协议 +ca证书)。Go 用 Eclipse Paho,注意SetClientID(device_sn)与SetUsername(token)。

十、Broker 选型:官方支持三类服务器

协议对 Broker 无绑定,官方明确支持接入阿里云 MQTT、腾讯云 MQTT、自建 MQTT、电信云。自建一条命令即可起一个 EMQX:

# docker-compose.ymlservices:emqx:image:emqx/emqx:5.6ports:-"1883:1883"# MQTT-"8883:8883"# MQTT over TLS-"18083:18083"# Dashboardvolumes:-./data:/opt/emqx/data

docker compose up -d即可。Mosquitto / HiveMQ / AWS IoT Core 同样适用。

十一、几个常见坑

  1. ClientID 用随机值→ 与设备侧会话互相踢下线。规范要求devicename与 DeviceName / ClientID 一致,ClientID 请用设备 SN。
  2. id用 64 位自增→ 超出 uint32 上限(4294967295)后溢出,防重打逻辑失效。
  3. 把 Topic 字符串当成协议标准→ 换 Broker 或换供应商时发现对不上。协议不固化主题,落地时务必写进部署文档。
  4. 图片 base64 塞进 JSON 票据→ 超过 60K 会报207/208。大图用type=6的 URL 模式。
  5. Clean Session 设成 false 却没做会话恢复→ 重连后收到一堆历史消息。初期保持默认(清除会话)。
  6. 生产用 1883 明文→ 数据裸奔。

相关能力:本文协议内容以商为《云打印机对接第三方 MQTT 通信协议》v2025-11-10 为准(HTTP 侧 v1.0)。该 MQTT 直连方案已有国内与海外用户商用运行;协议规范计划 2026-10 以 Apache 2.0 开放。XML / JSON 完整排版定义见官网文档中心:https://www.sw-aiot.com/doc.html

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询