资讯动态

S7-1200通过TSEND_C控制标签打印机:7004与ZPL-II避坑

发布时间:2026/10/28 3:25:04 来源:尧图企业网站定制
简介这份教程面向使用西门子 S7-1200 PLC 和博图软件、需通过以太网直接驱动霍尼韦尔或斑马标签打印机的自动化工程师与设备调试人员重点解决工控设备与打印机之间 TCP 通信建立、ZPL-II 指令转换及打印控制编程等问题。压缩包为 1 个 PDF 文件约 721KB内容围绕霍尼韦尔 PX240S 打印机的网络参数配置、ZSim 指令语言切换、标签设计导出 ZPL-II 代码以及在博图中调用 TSEND_C 建立连接、组织数据块和编写可变编码打印程序的完整思路展开。已有 1660 人学习说明该方案在同类现场调试中具有参考价值。读者可了解打印机端口需改至 5000 以下、连接状态码 7004 的含义、字符串与单字符发送的取舍、去优化数据块便于绝对寻址等细节并据此搭建可切换标签格式、按计数生成唯一编码的打印控制逻辑减少现场联调时的试错成本。1. 打印机不动先区分 7004 是正常状态还是连接卡死博图TIA Portal里挂上 TSEND_C编译不报错下载也不报错可标签打印机就是一点反应都没有在线诊断里状态码停在 7004——很多人第一反应是这串数字代表出错然后开始逐个参数乱改越改越乱。另一边还有一类反直觉的情况连接建起来了数据也发出去了西门子 PLC 那边 DONE 置位结果斑马打印机吐出一张白纸或者把 ZPL-II 源码原样打出来纸上印着^XA^FO40,40^A0N...这种东西。西门子 S7-1200 通过 TCP 控制霍尼韦尔标签机或斑马打印机这类活卡人的地方几乎不在梯形图逻辑本身而是集中在三处端口范围、打印机指令语言、以及 ZPL-II 字符串从博图里送出去时的字节组织方式。这三处任一处理错表现出来的现象都是同一个——没反应。2. TSEND_C 卡在状态码 7004连接阶段和数据阶段的判定2.1 7004 表示连接已建立等待发送作业先把最容易误判的一点说清楚。TSEND_C 的状态码里7004 的意思是「连接已经建立正在等待发送作业」它不是错误码而是一个中间态。你在博图里勾上监视如果 REQ 一直为 FALSESTATUS 就稳稳停在 7004一旦 REQ 出现上升沿状态会跳到 7005正在发送发送完成变成 7006然后回到 7004 等下一条。所以看到 7004 别急着改参数要先确认两件事连接对象到底通没通以及 REQ 有没有真的产生上升沿。真正麻烦的是 7004 之后不动、或者干脆连 7004 都到不了。这种情况下我一般按「谁主动、连谁、连哪个口」这三个问题倒查。TSEND_C 内部已经把建立连接、发数据、断连接打包了你只要给对 CONNECT 结构它自己会去握手。CONNECT 是一个 TCON_IP_v4 类型的结构体必须放在一个非优化的全局 DB 里长期存在不能塞进 FC 的临时变量——临时变量每个扫描周期都会重建连接握手刚走一半结构体内容就被覆盖表现出来就是状态在 7004 和 8085 之间反复横跳或者直接挂死。2.2 常见返回码对应关系下面这张表是我在现场遇到过、以及在同行群里被问得最多的几个码。不同固件版本的措辞可能略有出入遇到没见过的码直接查博图在线帮助里 TSEND_C 的状态码页那里列得比任何博客都全。状态码含义常见触发原因先查什么7004连接已建立等待发送作业REQ 无上升沿触发逻辑、HMI 按钮是否点动7005连接已建立正在发送数据正常过程态无需处理7006连接已建立发送完成正常过程态看 DONE 是否复位0000无错误正常态—8085建立连接失败对方无响应打印机 IP 不对、未开机、网线没插、端口无人监听ping、端口探测8087连接被对方拒绝或中止端口填了但打印机不监听该端口核对打印机端口设置8090连接 ID 非法或被占用同一 ID 被两个指令用、和 TCON 混用检查全部通信指令的 ID80A7通信超时网络时通时断、交换机问题抓包或长期 ping80C3连接已存在CONT 保持时重复触发建连检查 CONT 与 REQ 的组合表里 8085 和 8087 是新手最容易混淆的一对前者是「压根找不到人」后者是「找到了人但对方不开门」。前者去查 IP 和物理链路后者九成是端口号填错了或者打印机那边根本没启用该端口。2.3 一份能直接抄的 TSEND_C 调用下面这段是 SCL 里的调用骨架DB 里的 ZPL 指令和长度由前面的组包逻辑准备好这里只负责发。用 SCL 写是因为参数多梯形图上摆一堆引脚反而看不清。// 每条标签一个发送请求REQ 用上升沿触发避免一张标签连打多张 #bTrig(CLK : HMI.btnPrint); // HMI 打印按钮点动 DB_Comm.reqTSEND : #bTrig.Q; // 只在按钮按下的那一个扫描周期为 TRUE // TSEND_C 调用CONT 常 1 保持连接避免每条标签都重新握手 TSEND_C_DB(REQ : DB_Comm.reqTSEND, CONT : TRUE, // 常 TRUE连接保持 LEN : DB_Comm.iSendLen, // 本条 ZPL 的实际字节数 CONNECT : DB_Comm.tconIP, // TCON_IP_v4 结构放在非优化 DB DATA : DB_Comm.aSendBuf, // Array[0..999] of Byte DONE DB_Comm.doneSend, BUSY DB_Comm.busySend, ERROR DB_Comm.errSend, STATUS DB_Comm.statSend); // 发送成功后立刻清触发位为下一次上升沿做准备 IF DB_Comm.doneSend THEN DB_Comm.reqTSEND : FALSE; END_IF;几个参数值得单独说明。REQ必须是上升沿这是重复打印问题的总根源——如果直接把按钮的常开点接上去按钮按住期间每个扫描周期都会触发一次一张标签能给你连打几十张。CONT : TRUE表示连接建立后一直保持这样后续每条标签只发数据、不重新三次握手单条耗时能压到几十毫秒把 CONT 设成 FALSE 的话每发一条都要重连一次节拍慢不说打印机那边的连接槽位还可能来不及释放第二次就撞上 80C3。LEN填的是本条数据的有效字节数不是缓冲区总长度这一点在第四章会展开讲写错了就是打印内容截断。DATA我建议用Array[0..999] of Byte而不是直接给 String原因同样在第四章。CONNECT对应的 TCON_IP_v4 结构参数这样填// TCON_IP_v4 结构全局 DB 内定义属性里取消「优化的块访问」 DB_Comm.tconIP.InterfaceId : 64; // PLC 的 PROFINET 接口硬件标识符设备组态里查 DB_Comm.tconIP.ID : 1; // 连接 ID同一 CPU 内唯一 DB_Comm.tconIP.ConnectionType : 11; // 11 TCP DB_Comm.tconIP.ActiveEstablished : TRUE; // PLC 作为主动连接方 DB_Comm.tconIP.RemoteAddress : 192.168.2.88; // 打印机 IP DB_Comm.tconIP.RemotePort : 4000; // 打印机监听端口必须 ≤ 5000 DB_Comm.tconIP.LocalPort : 0; // 0 表示由系统自动分配InterfaceId这个值很多人随手填 64 是蒙对的正确做法是打开设备组态点 CPU 的 PROFINET 接口看属性里的硬件标识符填那个数。填错的典型现象是编译通过、下载正常运行时直接 8090。LocalPort填 0 让系统分配最省事如果某个固件版本不接受 0就填一个 2000 到 4999 之间的固定值别填 9100 之类的同样会踩端口范围的坑。3. 端口号超范围9100 与西门子 0–5000 的硬冲突3.1 打印机默认端口为什么在博图里天生连不上标签打印机出厂默认的原始打印端口通常是 9100部分型号还有 6101。而 S7-1200 的开放式以太网通信本地端口和远程端口的取值范围都限制在 5000 以内0 表示自动分配。这就意味着你在 TCON_IP_v4 的RemotePort里根本填不进 9100——即便某些版本的编辑器允许你敲进去下载运行后也建不起连接。这是最典型的「配置看着全对、就是不通」因为两边都没错错的是它们不在同一个可选范围里。解决办法只有一个方向把打印机侧的端口改到 5000 以下PLC 跟着改。常见做法是改成 4000、3000、2500 这类不冲突的端口号。改之前先确认这个端口没被产线上的其他设备占用同一网段里端口冲突比 IP 冲突更难查。3.2 改端口的正确顺序先打印机再组态最后下载顺序错了会浪费大量时间。我习惯按这四步走每步都有明确的验证点第一步进打印机的设置界面找到通信/端口配置页把原始打印端口从 9100 改成 4000保存并让网络服务重启。霍尼韦尔这类带触摸屏的机型是在触屏菜单里改带网页配置的机型用浏览器访问打印机 IP 进配置页改。第二步用 PC 做端口探测确认 4000 真的在监听。第三步回到博图改RemotePort重新编译下载。第四步在线监视状态码。第二步的验证命令Windows 上用 PowerShell 的就够# 测打印机端口是否真的在监听TcpTestSucceeded 为 True 才算通 Test-NetConnection -ComputerName 192.168.2.88 -Port 4000 # 顺便确认网络层通不通这一步通了不代表端口通 ping 192.168.2.88 -n 4Test-NetConnection的结果里重点看两行PingSucceeded和TcpTestSucceeded。ping 通但 TCP 不通说明网络层没问题问题在打印机没在那个端口上监听——要么端口没改成功要么改完没重启网络服务。两个都不通先查网线、交换机、IP 网段。我遇到过一次改完端口没断电重启配置写进了 flash 但没生效白折腾了半小时从那以后改完打印机网络参数一律断电重启一次。3.3 网关留空、掩码一致以及「ping 通」的误导性打印机的 IP、子网掩码要和 PLC 在同一网段网关默认留空。同网段通信本来就不经过网关但网关填错会带来一个隐蔽的问题某些打印机的网络栈在启动时会尝试向网关注册或者做路由探测网关不可达的情况下启动会变慢甚至短暂不响应连接请求表现出来就是「刚上电那两分钟连不上过一会儿又好了」这种玄学现象。所以别图省事乱填。还有一点必须提醒ping 通只证明 ICMP 可达不证明 TCP 端口可用。这两件事在网络层是分开的很多现场排查就卡在「能 ping 通啊」这句话上然后在 PLC 程序里翻来覆去改了半天其实问题一直在打印机那头。判断顺序永远是先 ping再测端口最后才动博图。4. 连接通了却打不出标签ZPL 指令、DB 寻址与 LEN 三处翻车4.1 ZPL 指令的正确来源打印到文件不是打印打印机能连上、数据也发出去了但纸上是空白或者直接印出指令源码第一个要怀疑的是打印机当前使用的指令语言。这类打印机出厂可能工作在自家的 PCL 或者别的描述语言下收到 ZPL 会当成普通文本原样输出于是你就看到纸上印着^XA。正确做法是进入系统设置找到常规里的命令语言选项切换成兼容 ZPL-II 的模式部分机型叫 ZSim 或 ZPL 兼容模式保存退出时选择保存。指令语言对了接下来是指令本身从哪来。别手写用标签设计软件画按实际标签纸尺寸和客户要求的编码规则排版字体、字号、条码类型都在图形界面里调。画完之后点打印但必须勾选「打印到文件」让软件生成一个指令文件而不是真的驱动打印机出纸。把生成的文件用记事本打开里面就是完整的^XA ... ^XZ指令段。^XA ^PW812 ^LL609 ^FO40,40^A0N,50,50^FDTEST-000000001^FS ^FO40,120^BY3^BCN,100,Y,N,N^FD000000001^FS ^XZ这段里几个字段值得记一下^PW是打印宽度点^LL是标签长度^FO是坐标偏移^A0N,50,50是字体和宽高^FD...^FS之间就是要打印的内容^BY3和^BCN是条码的窄条宽度和类型。PLC 要做的就是把^FD后面那串编码替换成变量其余部分原样不动。4.2 DB 去优化访问绝对寻址失效的根源这一步是纯纯的血泪经验。你在 DB 里定义好 ZPL 模板和缓冲区编译下载运行时 DATA 参数就是报错或者发出去乱码八成是因为这个 DB 还带着「优化的块访问」属性。优化访问的 DB 里变量没有固定的绝对地址编译器只保证符号访问而 TSEND_C 的 DATA 参数在底层是要拿一段连续内存的起始地址和长度你没法用P#DB1.DBX0.0 BYTE 200这种写法去指它。解决方式很直接打开 DB 属性取消勾选「优化的块访问」确定然后重新编译整个项目。改完之后在 DB 的监视界面才看得到偏移量列变量地址才固定下来。这个操作有个副作用——一旦取消优化DB 里所有变量的地址就固定了后续增删变量要重新编译并注意地址变化所以我在项目初期就把通信用的 DB 结构定好尽量少动。顺带说下 String 的坑。直接把String[200]类型的变量接到 DATA 上发出去的字节流前面会多出两个字节——String 的类型头最大长度和当前长度打印机收到这俩字节会当成无效指令轻则忽略重则把后续内容也当垃圾丢掉。所以我用Array[0..999] of Byte做发送缓冲区组包时就把 String 转成字节数组长度单独算发出去干干净净。4.3 LEN 与组包编码位数一改就截断LEN是 TSEND_C 最容易写错的一个参数。它要的是「本次要发送的字节数」不是缓冲区的容量也不是 DB 的总长度。很多人图省事直接填个 500 或者填缓冲区的上限结果就是每次发送都带上一大段填充字节打印机把填充字节当垃圾指令处理或者干脆因为收到超长数据包而丢弃整包。正确的做法是组包时就把有效长度算出来。下面这段 SCL 演示了按格式号选模板、拼流水号、算长度的完整过程// 组包按 HMI 选的格式号取对应 ZPL 模板拼上本次流水号 CASE HMI.iFormatNo OF 1: // 格式一9 位数字编码一维码 DB_ZPL.sZPL : DB_ZPL.sTplA DB_ZPL.sSerial9; 2: // 格式二长编码带前缀 DB_ZPL.sZPL : DB_ZPL.sTplB DB_ZPL.sSerial17; ELSE DB_ZPL.sZPL : DB_ZPL.sTplA DB_ZPL.sSerial9; END_CASE; // String 转字节数组避免 String 头两字节混进数据流 // Strg_TO_Chars 需 TIA V15.1 及以上更早版本改用 MOVE_BLK 从 String 的 2 字节处搬 Strg_TO_Chars(IN : DB_ZPL.sZPL, Cnt DB_ZPL.iCharCnt, CharsArray : DB_ZPL.aSendBuf); // 发送长度只取有效字符数绝不用缓冲区容量 DB_ZPL.iSendLen : DB_ZPL.iCharCnt; // 流水号累加超出位数范围回绕保证编码不重复也不溢出 DB_ZPL.dwSerial : DB_ZPL.dwSerial 1; IF DB_ZPL.dwSerial 999999999 THEN DB_ZPL.dwSerial : 1; END_IF;这里有几个细节。Strg_TO_Chars会把 String 的有效字符逐个搬到 Char 数组返回值Cnt就是实际字符数正好拿来当LEN。流水号用 DWord 累加回绕判断按编码位数来定上限九位编码就是 999999999订单量大的时候还要考虑当天清零或者按批次重置。模板字符串sTplA、sTplB本身也是在非优化 DB 里开机时从初始值加载一次后续由 HMI 选的格式号决定用哪个。编码位数如果客户要求从 9 位改成 12 位模板里的^FD字段长度和流水号上限都要同步改改漏了一处打出来的标签就是位数不对或者条码扫不出来。5. 避坑现场记录重复打印、连接占用、空白标签这五件事5.1 五条现象、原因与解决记录现象一点一次按钮打印机连吐十几张一模一样的标签。原因基本可以确定是 REQ 没做上升沿按钮按住期间每个扫描周期都在触发发送而打印机收到一条打一张就变成了连打。解决是在 REQ 前加一个上升沿检测或者把触发位在 DONE 之后立刻复位。现象二第一张打得好好的第二张开始状态码跳到 80C3 或者 8090。常见原因是同一连接 ID 被多个指令共用或者是 TSEND_C 和 TCON/TSEND 混着用——TSEND_C 内部已经管了建连和断连你再挂个 TCON 去抢同一个 ID两边打架。也可能是CONT设成了 FALSE每条发完就断连打印机侧连接槽位没及时释放。解决是统一用 TSEND_C连接 ID 全局唯一CONT常 TRUE。现象三纸打出来了上面印的是^XA^FO...源码。打印机指令语言没切到 ZPL 兼容模式把指令当普通文本渲染了。进系统设置的命令语言项切换保存后重启打印机。现象四标签内容比设计的短后半截没了。要么LEN填小了要么 DB 里 String 定义的长度装不下实际内容被截断要么编码位数改长了但模板里的字段没跟着改。排查方法是先在 PC 上用脚本把同样的 ZPL 直接发给打印机确认指令本身完整再回头查 PLC 侧的组包和长度。现象五中文标签打出来是问号或者空白。ZPL 内置字库不含中文字形直接写中文进^FD打不出来。要么在标签设计软件里把中文转成图形位图指令^GF嵌入要么用支持中文的字体模块。这一条在切换语言时最容易翻车设计阶段就得确认。5.2 上线前值得固定下来的三个习惯第一个习惯任何一条通信指令上线前先用 PC 直连打印机把 ZPL 跑通。这一步能把「指令问题」和「PLC 问题」彻底切开省掉大量在博图里瞎改的时间。用 Python 发一段就是几行代码的事import socket # 直接用 PC 绕过 PLC验证打印机是否吃这条 ZPL 指令 zpl b^XA\n^FO40,40^A0N,50,50^FDTEST-000000001^FS\n^XZ\n with socket.create_connection((192.168.2.88, 4000), timeout3) as s: s.sendall(zpl) # 发完即断打印机收到一条打印一条 print(sent, len(zpl), bytes)create_connection的两个参数是打印机 IP 和改好的端口sendall保证整段发完。这段脚本能跑通说明打印机、端口、指令语言、ZPL 内容全部正确剩下的问题一定在 PLC 侧。跑不通就别在 PLC 上浪费时间了。第二个习惯把所有通信相关的 DB 集中在一个组里统一取消优化访问并且在项目文档里记下每个 DB 的用途和长度上限。第三个习惯LEN永远由程序算绝不在参数上写死数字。6. 快速定位从「按了没反应」到「第一张标签吐出来」现场排这类故障最怕的是没有固定顺序东改一下西改一下改到最后自己都忘了哪个参数动过。我现在的固定套路是四层往下剥每层都有明确的判定信号剥到哪层断了就停在哪层不要跳。层级判定手段通过标志不通过时改什么网络层ping 打印机IP稳定回包无丢包网线、交换机、IP 与掩码端口层Test-NetConnection -Port 端口TcpTestSucceeded True打印机端口号、网络服务是否重启指令层PC 用 Python 直发 ZPL打印机正常出纸指令语言、ZPL 内容、标签尺寸程序层博图在线监视 STATUS 与 LEN7004 → 7005 → 7006 → DONEREQ 沿、CONNECT 结构、LEN、DB 优化属性四层里前三层全部通过、只剩程序层的时候排查范围其实已经很小了就那几个参数。我一般的顺序是先看 LEN 的实际值对不对在线改一下看 DONE 之后打印机有没有反应再看 REQ 有没有上升沿接着看 CONNECT 结构里的端口和 IP 有没有跟着打印机改动同步最后才去怀疑 DB 的优化属性。之所以把 DB 属性放最后是因为它一旦错了通常第一张标签就出不来而不是时好时坏症状比较有辨识度。还有一个很实用的技巧在博图里给 STATUS 加一个趋势记录或者简单的历史值捕获把最近几次发送的状态码和时间戳留下来。有些故障是间歇性的你盯着屏幕看的时候它偏偏是好的一转身就 8085 了。留一份状态历史比现场蹲守有效得多。法宝只有一个——把每次改动的参数记下来改一处验一处别一次动三个地方。从那以后我每次接手这类 TCP 打印项目都会先花十分钟把四层验证走一遍哪怕设备是刚出厂的新机、参数是别人配好交付的也照样走。踩过的坑告诉我省下的这十分钟最后往往要拿半天来还。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑