 MQTT 直連實(shí)戰(zhàn):完整字段表、JSON / XML 排版與防重打設(shè)計(jì))
云打印機(jī) MQTT 直連實(shí)戰(zhàn)完整字段表、JSON / XML 排版與防重打設(shè)計(jì)附 Python 示例大多數(shù)商用云打印機(jī)只有「設(shè)備 → 廠商云 → 你的系統(tǒng)」一條路數(shù)據(jù)必須經(jīng)過廠商。本文給出一條可私有化的路徑設(shè)備直連你自有的 MQTT Broker打印內(nèi)容全程不落廠商云。含官方通信協(xié)議v2025-11-10的完整下行字段表、XML 與 JSON 兩套排版示例、14 項(xiàng)上報(bào)狀態(tài)碼、id防重打設(shè)計(jì)以及 Broker 選型與接入要點(diǎn)。一、為什么需要「直連」市面上商用云打印機(jī)的默認(rèn)架構(gòu)是云中轉(zhuǎn)打印機(jī) ←→ 廠商云 ←→ 你的 SaaS / ERP這條鏈路的問題不在功能在合規(guī)與自主權(quán)金融 / 醫(yī)療 / 政務(wù)客戶的打印內(nèi)容單據(jù)、處方、憑條不得落到第三方服務(wù)器你的業(yè)務(wù)系統(tǒng)里設(shè)備是廠商的資產(chǎn)—— 換供應(yīng)商等于換掉全部設(shè)備廠商云故障 你的全部門店停擺。MQTT 直連把中間那一跳去掉打印機(jī) ←→ 你自有的 MQTT Broker設(shè)備只認(rèn) Broker 地址與憑證打印內(nèi)容不出你的服務(wù)器。協(xié)議規(guī)范MQTT 直連 / HTTP / 自定義指令計(jì)劃于 2026-10 以 Apache 2.0 開放屆時(shí)你可以自行實(shí)現(xiàn) Broker 端也可以在多家供應(yīng)商之間遷移而不必?fù)Q設(shè)備。云中轉(zhuǎn)與直連不是二選一 —— 成熟的方案兩套都支持按客戶合規(guī)等級切換。該直連方案目前已有國內(nèi)與海外用戶商用運(yùn)行。二、協(xié)議全貌三條約定 一種配置方式官方《云打印機(jī)對接第三方 MQTT 服務(wù)器通信協(xié)議》只約束三件事其余交給部署方項(xiàng)約定可接入的服務(wù)器類型阿里云 MQTT、騰訊云 MQTT、自建 MQTT、電信云等有定制需求可另行對接QoS訂閱主題與發(fā)布主題一律 QoS 1Clean Session設(shè)備默認(rèn)清除會話若希望設(shè)備接收離線期間的消息需在 PC 配置工具寫入?yún)?shù)時(shí)選擇「不清除 Session」參數(shù)寫入方式設(shè)備開機(jī)后通過USB 數(shù)據(jù)線連接 PC用《云打印設(shè)備配置工具》寫入 Broker 地址與憑證設(shè)備側(cè)覆蓋熱敏打印機(jī)、標(biāo)簽打印機(jī)、打印云盒三類形態(tài)。這里有一個(gè)容易誤解的點(diǎn)協(xié)議不定義「設(shè)備如何知道自己該連哪個(gè) Broker」—— 因?yàn)樗皇菂f(xié)議行為而是出廠 / 現(xiàn)場配置行為參數(shù)經(jīng) USB 寫入設(shè)備。所以部署流程里必然有一步「批量寫入?yún)?shù)」這步用官方 PC 工具即可完成不需要自己改固件。三、Topic 主題規(guī)范約定「方向」不固化「字符串」協(xié)議只約定主題的角色不約定具體字符串訂閱主題接收打印任務(wù)、語音播報(bào)消息、命令消息 —— QoS 1發(fā)布主題上報(bào)設(shè)備狀態(tài)與打印結(jié)果 —— QoS 1也就是說sw/xxx/task/down這類字符串不屬于協(xié)議定義范圍而由部署方自定義。下面給一套推薦命名方案工程建議非協(xié)議強(qiáng)制好處是便于通配符訂閱與分環(huán)境隔離推薦 Topic方向QoS用途{product}/{ver}/{sn}/task下行1打印任務(wù){(diào)product}/{ver}/{sn}/voice下行1語音播報(bào){product}/{ver}/{sn}/cmd下行1配置管理命令{product}/{ver}/{sn}/status上行1設(shè)備狀態(tài)與打印結(jié)果平臺側(cè)用通配符訂閱避免為每臺設(shè)備單獨(dú)建訂閱{product}/{ver}//status落地時(shí)請把主題字符串寫進(jìn)你的部署文檔 —— 因?yàn)閰f(xié)議不強(qiáng)制跨供應(yīng)商遷移時(shí)這部分要重新約定這是直連方案需要提前規(guī)劃的一點(diǎn)。四、下行打印與語音消息字段表服務(wù)器推送到設(shè)備的消息為 UTF-8 JSON。完整字段如下官方 v2025-11-10 規(guī)范關(guān)鍵字類型必填說明iduint32是打印任務(wù) ID服務(wù)端確保不重復(fù)用于防重復(fù)打印。設(shè)備上報(bào)結(jié)果以此 ID 標(biāo)識任務(wù)。取值范圍0 – 4294967295typeint是打印數(shù)據(jù)排版類型打印語言見下表contentsString / Array / Object是打印內(nèi)容數(shù)據(jù)格式與type保持一致pWidthint否打印紙張寬度 mm58 / 80 / 110默認(rèn) 58pCopyint否打印份數(shù)默認(rèn) 1pTypeint否紙張類型默認(rèn) 1紙張為標(biāo)簽時(shí)必傳vTypeint否播報(bào)類型vMessagestring否語音播報(bào)文本TTS 合成或音頻下載 URLvModeint否播報(bào)排隊(duì)策略默認(rèn) 0messagestring否預(yù)留 HTTP 下載類型的打印內(nèi)容與contents二選一優(yōu)先取contentsgatewaystring否僅打印云盒usb發(fā)往 USB 打印機(jī)192.168.2.11發(fā)往指定局域網(wǎng) IP 打印機(jī)type取值值含義contents形態(tài)1ESCBASE64 編碼后數(shù)據(jù)字符串2CPCL字符串3TSPL字符串4XMLUTF-8字符串5JSONUTF-8Array小票/ Object標(biāo)簽6PNGURL 鏈接或 base64 內(nèi)容字符串99打印設(shè)備配置管理Object其中type4的 XML 又分兩種設(shè)計(jì)基礎(chǔ) XML兼容市面各運(yùn)打印平臺矢量打印 XML是專為全球語言打印設(shè)計(jì)的排版框架。XML 與 JSON 的完整排版定義見官網(wǎng)文檔中心https://www.sw-aiot.com/doc.html。pType取值1 連續(xù)紙默認(rèn)2 標(biāo)簽紙3 黑標(biāo)紙4 穿孔紙8 連續(xù)紙用標(biāo)簽指令打印小票vType取值-1 無播報(bào)0 定制語音配合vMessage1 播報(bào)「你有新的訂單」10000 指定語音鏈接下載播報(bào)vMode取值0 按隊(duì)列順序播報(bào)任務(wù)先插入隊(duì)列默認(rèn)1 立即播報(bào)2 立即播報(bào)并清空待播報(bào)隊(duì)列五、下行示例均取自官方規(guī)范5.1 XML 排版type4—— 58mm 小票{id:12345678,type:4,pType:1,pWidth:58,pCopy:1,vType:0,vMessage:測試語音,contents:CB收款小票/CBBR商戶存根 請妥善保管BR--------------------------------BR商戶名稱 小熊每日鮮水果店BR商戶編號 1005800029BR交易時(shí)間 2020-12-11 10:23:30BR交易訂單號 1005800012112007217BR支付單號 202012112200140385141256912BR優(yōu)惠金額 5.00BR備注 測試小票BR交易狀態(tài) 收款成功BR支付方式 支付寶-公眾號支付BR交易金額 126.50 元BRBRQR10058001200047217/QRBR--------------------------------BR打印時(shí)間 2020-11-12 11:02:36 簽名BR}常用 XML 標(biāo)簽BR換行CB居中放大B放大一倍C居中L字體變高一倍W字體變寬一倍BOLD加粗RIGHT右對齊CUT切紙PLUGIN錢箱或外置音響IMGPNG 圖片 URLQR二維碼單個(gè)訂單最多打印一個(gè)BC128_A/BC128/BC128_C條碼A 為數(shù)字 大寫字母混合58mm 最多 14 位C 為純數(shù)字5.2 JSON 排版 · 小票type5contents為 Array{id:12345678,vType:0,vMessage:這是測試語音,pType:1,pWidth:58,pCopy:1,type:5,contents:[{cont:云打印 JSON 排版,type:title},{thead:{名稱:40%,單價(jià):25%,數(shù)量:15%,金額:20%},tbody:[[云打印機(jī)4G/WiFi,100,1,100],[4G 音箱攝像頭,999.44,1,999.44]]},{cont:合計(jì):2009.1 元,size:11,align:left},{both_sides:[收款金額,258.00]},{both_sides:[支付方式,微信支付]},{cont:1234567890,type:qrcode,align:center,size:60},{cont:1,type:cut}]}小票元素typetitle居中放大標(biāo)題text文本行cont可為字符串或數(shù)組png圖片最大 60Kbmp圖片最大 6Kqrcode最大 512 字節(jié)bc128默認(rèn) CODE128A/bc128a/bc128cdiv_line-分割線支持居中插字div_star*分割線cutcont為0全切 /1半切plugin開錢箱通用屬性alignleft 默認(rèn) / center / rightboldtrue / falsesize兩位數(shù)字ABA 為倍寬、B 為倍高各取 0–7both_sides第一個(gè)字符串左對齊、第二個(gè)右對齊theadtbody多列表格thead可傳對象映射列寬占比也可傳數(shù)組只給占比5.3 JSON 排版 · 標(biāo)簽type5contents為 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,...}]}}標(biāo)簽頂層參數(shù)SIZE[寬, 高] mmDIRECTIONDENSITYSOUNDlabel元素?cái)?shù)組。標(biāo)簽元素類型TEXTx/y/font/w/h/r/contQRCODEe糾錯(cuò)等級、w尺寸BC128h高度、show是否顯示可讀字符、narrow/wide條寬比BARw/h畫線BITMAPbase64 BMP同一份 JSON 載荷在HTTP 推送與MQTT 直連兩條通路上完全通用 —— 你可以先用 HTTP 跑通原型量上來后切 MQTT業(yè)務(wù)側(cè)序列化代碼一行不用改。六、下行遠(yuǎn)程配置管理type99設(shè)備支持云端遠(yuǎn)程設(shè)置音量、重啟、切換默認(rèn)指令模式{id:12345678,type:99,contents:{cmd:volume,value:1}}命令載荷說明volume{cmd:volume,value:1}音量0 靜音 / 1 低音量 / 2 中音量 / 3 大音量 / 4 最大reboot{cmd:reboot}系統(tǒng)重啟label_cmd{cmd:label_cmd,print:tspl}默認(rèn)標(biāo)簽指令可選tspl/cpcl/zpl默認(rèn) tsplprint_cmd{cmd:print_cmd,cmd:tspl}設(shè)置打印機(jī)默認(rèn)語言可選tspl/cpcl/esc七、上行狀態(tài)與打印結(jié)果上報(bào)設(shè)備通過發(fā)布主題上報(bào)UTF-8 JSON關(guān)鍵字類型說明devicenamestring云打印設(shè)備 SN機(jī)器碼與三元組的 DeviceName 或 ClientID 一致iduint32打印任務(wù) ID與服務(wù)器下發(fā)的任務(wù) ID 一致上報(bào)設(shè)備狀態(tài)時(shí)不帶此字段注意區(qū)分codeint結(jié)果碼code狀態(tài)碼code含義0打印成功 / 打印機(jī)正常100未知錯(cuò)誤通訊異常101打印機(jī)缺紙102打印機(jī)開蓋103打印機(jī)過熱201沒有id202沒有type203無效type參數(shù)未在定義范圍204沒有 msg205無效 msg如鏈接非標(biāo)準(zhǔn) HTTP206下載錯(cuò)誤重復(fù)下載 6 次207任務(wù)長度超出設(shè)備緩沖區(qū)208內(nèi)容格式錯(cuò)誤209重復(fù)任務(wù)不執(zhí)行打印根據(jù) ID 判斷三類上報(bào)樣例// 4G 機(jī)型開機(jī)連上 MQTT 服務(wù)后上報(bào)一次imei 為 15 位數(shù)字iccid 為 20 位卡號{devicename:dev1,imei:123456789012345,iccid:12345678901234567890}// 設(shè)備狀態(tài)上報(bào)缺紙不帶 id{devicename:dev1,code:101}// 打印結(jié)果上報(bào)任務(wù) 123456789 打印成功{devicename:dev1,id:123456789,code:0}帶掃碼功能的產(chǎn)品另有一路上報(bào){devicename:dev1,scan_content:abcd1234}。八、防重打設(shè)備是最后一道閘自建鏈路最容易踩的坑是同一張單子打兩遍。這套協(xié)議把防線放在兩層服務(wù)端id由服務(wù)端保證不重復(fù)下發(fā)前登記落庫唯一索引。設(shè)備端內(nèi)置防重打 ——同一id在不斷電情況下只打印一次重復(fù)推送時(shí)直接回報(bào)code 209重復(fù)任務(wù)不執(zhí)行打印。# 服務(wù)端下發(fā)前登記落庫唯一索引列名對應(yīng)協(xié)議里的 id# CREATE UNIQUE INDEX uk_mqtt_id ON print_tasks(mqtt_id);# 設(shè)備端命中已執(zhí)行表 → 不打印回報(bào) {devicename:...,id:id,code:209}配套工程約定設(shè)備只在打印完成后上報(bào)結(jié)果回報(bào)體里的id與下發(fā)一致服務(wù)端收到該id且code 0才把任務(wù)置為「已完成」未收到回報(bào)的任務(wù)超時(shí)后重新入隊(duì)并復(fù)用同一個(gè)id不要新建 ID—— 即使重復(fù)到達(dá)設(shè)備側(cè)防重打會自動跳過。??id是uint32上限 4294967295。若沿用雪花 ID 之類的 64 位方案務(wù)必先截?cái)嗷蜃鲇成浞駝t溢出后防重打邏輯會失效。九、鑒權(quán)與部署要點(diǎn)等級方案適用L1 基礎(chǔ)Username 設(shè)備 TokenPassword 可空內(nèi)網(wǎng) / 測試L2 增強(qiáng)Password HMAC-SHA256(device_secret, {ts}\n{nonce}\n{device_sn})公網(wǎng)生產(chǎn)L3 強(qiáng)安全mTLS 客戶端證書CN{device_sn}, O{customer_id}金融 / 醫(yī)療 / 政企生產(chǎn)環(huán)境必須 TLS8883這條沒有妥協(xié)余地。其余建議值端口1883TCP僅限內(nèi)網(wǎng)/ 8883TLS生產(chǎn)必須Keep Alive 60sClientID 建議使用設(shè)備 SN —— 規(guī)范要求devicename與三元組的 DeviceName 或 ClientID 一致用隨機(jī) ClientID 會與設(shè)備側(cè)會話互相踢下線單條載荷做上限控制JSON 票據(jù)里png≤ 60K、bmp≤ 6K、qrcode≤ 512 字節(jié)超限會命中code 207超緩沖區(qū)或208格式錯(cuò)誤。大圖請走type6傳 URL。最小可運(yùn)行示例Python Pahoimportjsonimportpaho.mqtt.clientasmqtt BROKER,PORTbroker.example.com,8883DEVICE_SNSW-03DW-ABC123TOPIC_TASKfswprint/v1/{DEVICE_SN}/task# 下行設(shè)備訂閱TOPIC_STATUSfswprint/v1/{DEVICE_SN}/status# 上行設(shè)備發(fā)布clientmqtt.Client(client_idDEVICE_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,qos1)# 接收打印任務(wù) / 語音 / 命令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_asciiFalse),qos1)defon_message(c,userdata,msg):# 設(shè)備側(cè)上報(bào){devicename:...,id:100001,code:0}print(msg.topic,msg.payload.decode(utf-8))client.on_connecton_connect client.on_messageon_message client.connect(BROKER,PORT,keepalive60)client.loop_forever()Node.js 側(cè)同理mqtt包mqtts://協(xié)議 ca證書。Go 用 Eclipse Paho注意SetClientID(device_sn)與SetUsername(token)。十、Broker 選型官方支持三類服務(wù)器協(xié)議對 Broker 無綁定官方明確支持接入阿里云 MQTT、騰訊云 MQTT、自建 MQTT、電信云。自建一條命令即可起一個(gè) EMQX# docker-compose.ymlservices:emqx:image:emqx/emqx:5.6ports:-1883:1883# MQTT-8883:8883# MQTT over TLS-18083:18083# Dashboardvolumes:-./data:/opt/emqx/datadocker compose up -d即可。Mosquitto / HiveMQ / AWS IoT Core 同樣適用。十一、幾個(gè)常見坑ClientID 用隨機(jī)值→ 與設(shè)備側(cè)會話互相踢下線。規(guī)范要求devicename與 DeviceName / ClientID 一致ClientID 請用設(shè)備 SN。id用 64 位自增→ 超出 uint32 上限4294967295后溢出防重打邏輯失效。把 Topic 字符串當(dāng)成協(xié)議標(biāo)準(zhǔn)→ 換 Broker 或換供應(yīng)商時(shí)發(fā)現(xiàn)對不上。協(xié)議不固化主題落地時(shí)務(wù)必寫進(jìn)部署文檔。圖片 base64 塞進(jìn) JSON 票據(jù)→ 超過 60K 會報(bào)207/208。大圖用type6的 URL 模式。Clean Session 設(shè)成 false 卻沒做會話恢復(fù)→ 重連后收到一堆歷史消息。初期保持默認(rèn)清除會話。生產(chǎn)用 1883 明文→ 數(shù)據(jù)裸奔。相關(guān)能力本文協(xié)議內(nèi)容以商為《云打印機(jī)對接第三方 MQTT 通信協(xié)議》v2025-11-10 為準(zhǔn)HTTP 側(cè) v1.0。該 MQTT 直連方案已有國內(nèi)與海外用戶商用運(yùn)行協(xié)議規(guī)范計(jì)劃 2026-10 以 Apache 2.0 開放。XML / JSON 完整排版定義見官網(wǎng)文檔中心https://www.sw-aiot.com/doc.html