资讯动态

S7-1200 PLC打标签:TCP发送ZPL-II与TSEND_C实践

发布时间:2026/10/28 3:27:05 来源:尧图企业网站定制
简介这份PDF教程面向使用西门子S7-1200与博图平台、需通过以太网TCP通信驱动霍尼韦尔或斑马标签打印机的自动化工程师及设备调试人员重点解决打印机网络参数设置、ZPL-II指令获取和PLC端打印控制程序编写等问题。资源共1个PDF文件压缩包约721KB篇幅集中适合现场快速查阅内容围绕霍尼韦尔PX240S等带网口机型展开涉及ZSim指令语言、Zebra Designer导出ZPL-II代码、TSEND_C通信组态、字符串与DB块数据组织、触摸屏格式切换以及编码测试等关键环节。已有1660人学习说明其在工控打印联调场景中具有一定参考价值。读者可从中获得从打印机参数配置到PLC发送指令的完整思路理解端口号、状态码与绝对寻址等易错点并借鉴中间转存DB和格式代号管理的做法提升现场联调效率。1. 一条网线替代并口PLC 打标签这件事本质是往 5000 以下的端口灌字符串第一次接到「PLC 打标签」的需求大多数人的第一反应是去找打印机驱动或者在博图里翻有没有现成的打印指令块。两条路都走不通——标签打印机在工业现场不是电脑外设它是一个挂在以太网上的指令解析器。你在博图 1200 里要做的是用 TCP 通信往它的某个端口写一段纯文本这段文本就是 ZPL-II 指令流。斑马打印机长期占据这套指令标准的事实位置霍尼韦尔 PX240S 这类全工业机型把指令语言切成 ZSim 就能吃同一套指令这就是为什么一份 PLC 程序能同时伺候两个牌子换机器基本不用改代码。下面这条链路我在几个现场都跑过一遍打印机侧改三个参数、PC 上把标签设计稿导成指令、博图里配 TSEND_C、最后把可变字段做成占位符交给 PLC 填。顺序不能乱尤其是端口那一步九成的「连不上」都卡在这里。2. 打印机侧只改三处IP、端口上限 5000、指令语言 ZSim拿到一台新打印机先别急着接线到 PLC。让它先能被一台笔记本正常通信把「打印机自身能不能吃指令」这件事单独验证掉后面排错才有对照。这一章的三处设置改完之后打印机就不再是「需要驱动的外设」而是一个监听端口的服务端。2.1 端口从 9100 挪到 5000 以下这是硬约束不是建议标签打印机出厂默认的 raw 打印端口基本都是 9100这是行业沿用了很多年的惯例PC 直连打印完全没问题。但 S7-1200 的开放式以太网通信在工程实践中对远程端口有范围限制按 0–5000 来处理最稳妥。你把 9100 填进 TSEND_C 的连接参数里CONNECT 阶段就过不去现象是连接一直建立不起来STATUS 停在 7002 或者干脆报连接相关的错误码而打印机面板上什么都看不到——它根本没收到任何东西。改法在触摸屏界面上通信设置里配置通信地址IP 和子网掩码按现场网段填网关留空。同一网段直连不需要网关填了反而可能因为路由判断出岔子。端口号那一项从默认值改成 4999 或者 5000 以下任意一个没被占用的值我一般用 4999离上限留一个数的余量避免某些固件对边界值处理不干净。参数项建议值说明IP 地址与 PLC 同网段如 192.168.2.88不要用 DHCP现场固定分配子网掩码255.255.255.0与 PLC 保持一致网关留空同网段直连无需网关通信端口4999必须小于 5000避开默认 9100指令语言ZSim兼容 ZPL-II 指令集改完退出时会有保存确认选「是」。这一步看着简单但我见过有人在面板上改完没确认保存重启后回到 9100然后对着博图查了一下午。改完的第一件事是把笔记本网口改到同一网段浏览器输入打印机 IP能打开它的 Web 配置页就说明网络层通了。提示改端口前先确认现场没有别的系统正连着这台打印机的 9100。端口一改原来那套 PC 端打印软件会立刻失效这是不可逆的切换动作选在停机窗口做。2.2 命令语言切到 ZSim跨品牌复用就靠这一步这一步是整个方案能成立的前提。打印机默认的命令语言通常是厂商自己的一套或者 PCL 类指令你发 ZPL 过去它不认识表现就是收到数据但不出纸或者打出一堆乱码字符。进系统设置常规命令语言选择 ZSim。ZSim 是兼容 ZPL-II 的指令解释器斑马那套 ^XA … ^XZ 的结构它能原样解析。切换完同样是保存退出。之后必须做一次独立验证别跳过用笔记本直连打印机随便发一段最小指令流看它出不出纸。验证通过说明打印机侧的 IP、端口、指令语言三件事都对了后面所有问题都可以归到 PLC 那一侧排错范围直接砍掉一半。# 打印机侧独立验证绕开 PLC直接从 PC 发一段最小 ZPL # 这一步只验证打印机能不能吃指令不涉及任何 PLC 逻辑 import socket zpl ^XA^CI28^FO40,40^A0N,48,48^FDVERIFY-000000001^FS^XZ s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(3) s.connect((192.168.2.88, 4999)) # 端口必须是改过的那个不是 9100 s.sendall(zpl.encode(utf-8)) # ZPL 是纯文本按 UTF-8 编码发送 s.close() print(sent)这段代码里三个参数都有讲究。connect的目标端口必须是你改过的 4999用 9100 会直接抛连接超时settimeout设 3 秒避免打印机不在线时脚本挂死encode(utf-8)是为了和后面指令里的^CI28对应上如果你后面要打中文编码方式和^CI必须成对设置混着来就是乱码。跑通这段纸出来了打印机这一侧就算彻底交付剩下的全是博图里的活。3. 把标签设计稿压成一段 ZPL-II打印到文件与占位符预留PLC 里不适合做排版。你不可能在 SCL 里算字号、算条码模块宽度、算标签边距。正确顺序是在 PC 上用标签设计软件把版式调好导出成指令流PLC 只负责往里面填可变字段。这一章讲怎么导出、怎么读懂导出的东西、怎么给它留口子。3.1 必须走「打印到文件」直接打纸拿不到任何东西标签设计软件用厂商提供的试用版或免费版就够这个流程只用到一次导出功能不需要长期授权。进去之后按实际标签纸设置三个基础参数标签宽度、高度、分辨率。分辨率这一项最容易埋雷——203 dpi 和 300 dpi 的机器同样的物理尺寸换算成的点数是完全不同的^PW打印宽度和^LL标签长度填错了打出来要么被裁掉一半要么下半张空白。排版时的几个约束要提前想清楚条码类型和模块宽度按客户编码规则定^BY后面三个数字分别是模块宽度、宽条比例、条码高度模块宽度太小低于 2在工业标签上扫不出来。字体用内置字体最稳^A0N后面跟的是字高和字宽两个值给一样就是等宽效果。编码方式选固定式不要在设计软件里开序列化。序列化交给 PLC 做否则软件自动递增和 PLC 写入会打架。可变字段的位置先留成固定的示例值比如000000001长度按最终编码位数给足。版式确认无误后点打印在打印对话框里勾上「打印到文件」保存成 .prn 或 .txt。这一步的产物是纯文本记事本就能打开。3.2 拆开那段文本骨架是指令肉是要替换的字段打开导出的文件你会看到一堆以 ^ 开头的指令夹杂着坐标数字。别被吓到实际有用的就那几条其余是字体缓存和图形资源。把关键结构抽出来其余可以精简掉——指令流越短PLC 的字符串拼接越省事发送字节数也越少。^XA ^FX 报文开始 ^FS ^CI28 ^FX 字符集设为 UTF-8打中文必须 ^FS ^PW812 ^FX 打印宽度 812 点203dpi 下的 4 英寸 ^FS ^LL1218 ^FX 标签长度 1218 点 ^FS ^FO40,40^A0N,48,48^FDNO:^FS ^FX 固定文本左上角坐标 40,40 ^FS ^FO40,120^A0N,64,64^FD%SERIAL%^FS ^FX 可变编码占位符 %SERIAL% ^FS ^FO40,240^BY3^BCN,120,Y,N,N^FD%SERIAL%^FS ^FX Code128 条码内容同编码 ^FS ^XZ ^FX 报文结束缺这条打印机不执行 ^FS这里的关键动作是把原来示例编码所在的位置换成%SERIAL%这个占位符。为什么用占位符而不是直接算偏移量去改字符因为标签上编码可能出现两次人眼可读文本 条码内容用偏移量改要维护两处地址一旦版式微调两个偏移全变。用占位符PLC 侧只要做一次全局替换版式怎么改都不影响程序逻辑。%SERIAL%这个写法本身没有魔法它只是个不会和 ZPL 指令冲突的字符串。你在 PLC 里用字符串替换函数把它换掉就行。要注意替换后的总长度必须和占位符占用的位置对齐——如果替换结果比占位符长后面的字符会被顶掉短了则会留下残字符。所以占位符的长度要按最终编码的最长可能位数来给9 位编码就写 9 个字符的占位符或者干脆用定长替换。指令作用调参建议^XA/^XZ报文起止缺 ^XZ 打印机不执行缓存住等后续数据^CI28字符集 UTF-8打中文必设纯数字可省^PW/^LL打印宽度 / 标签长度按 dpi 换算203dpi 下 1 毫米约 8 点^FO x,y字段左上角坐标单位是点改位置只动这里^A0N,h,w内置字体及字高字宽两值相等为等宽h 建议不小于 30^BYw,r,h条码模块宽 / 宽条比 / 高w 不低于 2工业标签建议 3^BCNCode128 条码后三个参数控制可读文本与校验^FD/^FS字段内容 / 字段结束可变内容全部落在 ^FD 里4. 博图里 TSEND_C 的组态细节CONNECT 参数、DB 去优化与 7004 状态码到这一步打印机已经能被 PC 使唤了接下来把它接到 1215C 上。S7-1200 的开放式用户通信有 TCON、TSEND、TRCV 一组指令也有把它们打包好的 TSEND_C。带连接管理的场景我一般直接用 TSEND_C因为它把建立连接、发送、断开三件事合到一个背景数据块里少维护一个连接 DB。4.1 引脚含义与连接建立顺序别把 7004 当成发送成功TSEND_C 的调用不复杂复杂的是对状态位的理解。CONT : TRUE表示保持连接第一次REQ上升沿时建立连接并发送之后连接复用。CONNECT引脚指向一个 TC_CON 类型的数据结构里面是 IP、端口、连接类型这些硬参数。// 打印发送TSEND_C 调用 // ZPL_Buffer 为去优化的全局 DBZPL_Str 存放完整指令流 TSEND_C_DB(REQ : Send_Trigger, // 上升沿触发一次发送 CONT : TRUE, // 保持连接不每次重连 LEN : ZPL_Buffer.Send_Len, // 发送字节数不含 String 头 CONNECT : TCP_Conn_Printer, // TC_CON 结构见下表 DATA : ZPL_Buffer.ZPL_Str, // VARIANT可给 String 或 Byte 数组 DONE Send_Done, BUSY Send_Busy, ERROR Send_Error, STATUS Send_Status);LEN这个参数是新手最容易填错的地方。当DATA给的是 String 类型时String 变量前面有两个字最大长度、当前长度实际字符从第三个字节开始所以LEN要按字符数算不能直接拿 String 的声明长度去填。我一般单独维护一个Send_Len变量在拼装完指令流之后用LEN()取当前长度写进去保证发出去的字节数刚好是指令流的长度不多不少。多发几个字节打印机会把多余内容当成下一条指令的开头缓存住不出纸。连接参数放在一个 TC_CON 结构里几个字段必须和硬件组态对上参数典型值说明InterfaceId64CPU 集成 PROFINET 口的硬件标识符看设备组态ID1连接号同一 CPU 内唯一即可ConnectionType16#0BTCP别选成 ISO-on-TCPActiveEstablishedTRUEPLC 主动连打印机打印机是被动端RemoteAddress192.168.2.88打印机 IP四字节分别填RemotePort4999与打印机侧改过的端口一致LocalPort2000本地端口任意未被占用的值ActiveEstablished : TRUE这一项决定了谁是客户端。打印机作为服务端监听端口PLC 作为客户端主动发起这个方向不能反——大多数标签打印机不支持主动向外连接。指令执行起来之后STATUS会给出一个十六进制状态码。连接建立成功、还没有发送作业时状态码是 7004这个值的含义是「连接已建立等待发送」。很多人第一次看到 7004 就以为发完了其实它只说明 TCP 握手成了数据一个字节都还没出去。真正的发送结果要看DONE、BUSY、ERROR三个位BUSY为真表示正在发DONE一个扫描周期为真表示这一帧交出去了ERROR为真才需要去查STATUS。4.2 数据块去优化是硬要求String 和 Char 数组各有取舍原文里那句「注意去优化访问块」不是可选项。S7-1200 默认新建的 DB 是「优化的块访问」变量在内存里按类型对齐排布没有固定偏移地址。这种块没法用绝对地址访问跟某些老指令、指针操作、以及跨块的数据搬运配合起来会出问题。做法是在 DB 属性里取消勾选「优化的块访问」编译后每个变量就有确定的偏移量了。存储结构上我一般建两个 DB一个模板 DB 放不同标签格式的 ZPL 指令流每条一个 String长度按最长的那条给一个工作 DB 放当前要发的那一条和长度。切换标签格式时从模板 DB 按格式代号把对应的 String 整条搬过来改掉里面的占位符再触发发送。这样触摸屏上只需要选一个格式代号不用维护多套发送逻辑。String 和 Char 数组的选择取决于你怎么改内容方式优点缺点适用String拼接、替换函数直接可用长度自适应带 2 字节头LEN 要单独算整条指令流替换占位符方案Char 数组无头字节偏移确定单字符寻址方便长度要人工维护拼接麻烦只改几个固定位置的字符单个字符逐个写入的方式改编码时寻址简单——你知道编码在第几个字节直接写就行。但一条完整 ZPL 指令流动辄两三百字节如果每个字符都要单独赋值程序会长到没法维护。所以实际工程里常见做法是混合整条指令流用 String 存储和发送编码字段在 String 里用占位符定位替换时走字符串函数。只有在对时序要求极严、或者字节数必须精确控制的场合才退回到 Char 数组逐字节写。注意去优化之后DB 里变量的顺序就是你声明时的顺序中间插入一个新变量会让后面所有偏移量平移。已经上线的项目要加变量加在末尾别插在中间。5. 五个让打印机一声不吭的坑从端口 9000 到块访问优化这套方案跑通之后回头看出问题的地方集中在几个固定的位置。下面五条是我实际踩过的每条都按现象、原因、解决三段记遇到问题直接对号入座。坑一CONNECT 一直不建立STATUS 停在 7002打印机面板毫无反应现象是触发发送后BUSY一直不置位ERROR也不亮状态码卡在 7002 附近打印机侧什么动静都没有。原因是连接参数里的远程端口还是打印机默认的 9100超出了 S7-1200 开放式通信能处理的端口范围握手包发出去就被丢弃。解决是把打印机端口改到 5000 以下我用 4999同时把 TC_CON 的RemotePort改成同一个值两边必须一致改一边没用。改完记得给 PLC 重新下载硬件组态只改 DB 里的值不生效。坑二连接建立成功、数据也发了打出来全是乱码或者半个标签现象是DONE亮了纸也出了但内容不对要么全是符号要么只打了上半截。原因通常是两个。一是打印机命令语言还没切到 ZSim它用自己那套指令集去解释 ZPL逐字节对不上。二是^PW和^LL跟实际 dpi 不匹配203 dpi 的机器用了 300 dpi 的宽度点数图像被裁。解决是先回打印机确认命令语言是 ZSim再把设计软件里的分辨率设成和机器一致重新导出一遍指令流。别在旧指令流上手工改数字容易漏。坑三DB 没去优化发出去的是一段错位的垃圾现象是逻辑上什么都没错指令流在监控表里看着也正常但打印机收到的内容缺头少尾。原因就是 DB 还挂着「优化的块访问」String 的实际内存布局和你以为的偏移不一致DATA引脚拿到的指针指向的位置偏了。解决是把存放指令流的 DB 属性里那个勾去掉重新编译下载。这个坑的隐蔽之处在于你在博图里监控 String 变量显示的内容是完整正确的只有真正发出去才暴露问题。坑四编码位数一改标签上的号就被截断或者后面多一串 0现象是客户要求从 9 位编码改成 12 位程序里数字部分改了打出来却是前 9 位加三个空格或者干脆只显示前几位。原因是 ZPL 指令里^FD和^FS之间那段内容的长度是按 9 位预留的替换时新内容比占位符长把后面的^FS顶掉了字段没有结束符打印机会一直吃后面的字节直到缓存满。解决是把占位符按最长可能位数预留或者在替换前做长度校验新编码长度超过预留长度就直接报错不发出去。改位数这件事涉及 ZPL 模板和 PLC 拼装逻辑两处必须同步改。坑五中文标签打出来是问号或者方块现象是数字和英文正常中文全变乱码。原因是 ZPL 默认字符集不是 UTF-8而 PLC 侧的字符串按 UTF-8 编码发出去两边对不上。解决办法是在指令流开头加^CI28把打印机字符集切到 UTF-8同时确认打印机固件里装了中文字库。有些机型需要单独下载字库文件这个要查具体型号的说明。另外 S7-1200 的 String 是单字节的中文在 PLC 里本身就是多字节序列拼接时按字节算长度不要按字符数算。6. 编码自己长出来9 位序列号的拼装、补零与发送结果校验编码生成这块现场最常见的需求是「按顺序递增、定长、不能重复」。9 位数字编码从 000000001 开始每天或者每批归零。PLC 里做这件事本身不难难在补零和长度校验写不好就会出现 1、2、3 这样的变长输出条码扫描器读出来的位数和客户系统对不上。我的做法是维护一个 DINT 类型的计数器转成字符串之后用CONCAT在前面补固定数量的 0再用RIGHT截取末 9 位。这样无论计数值是 1 还是 123456789输出永远是 9 个字符。// 9 位定长编码生成先补足前导零再取末 9 位 DB_Code.Number : DB_Code.Number 1; // 计数递增 #sTmp : CONCAT(IN1 : 000000000, // 9 个 0 打底 IN2 : DINT_TO_STRING(DB_Code.Number)); DB_Code.Serial : RIGHT(IN : #sTmp, L : 9); // 取末 9 位定长输出CONCAT的输入必须是 String所以数字要先DINT_TO_STRING转一次前面补的 0 的个数要等于目标位数补 9 个 0 再取末 9 位等价于左补零到 9 位。计数器溢出前要归零否则DINT_TO_STRING会吐出 10 位数字RIGHT截完就丢了最高位编码重复。归零逻辑按业务来按天归零就在日期变化时清零。拼装完整指令流的时候把模板从格式 DB 搬过来用REPLACE把%SERIAL%换成刚生成的编码。这里有个前提替换前后长度必须相等。%SERIAL%是 8 个字符而 9 位编码是 9 个字符长度不等REPLACE会报错或者截断。所以模板里的占位符要按最终编码位数来定9 位编码就用 9 个字符的占位符比如#########替换时长度刚好对上。发送结果的校验我不看 7004只看 DONE 和 ERROR 的组合状态组合含义处理动作BUSY 1正在发送等待不要重复触发 REQDONE 1 且 ERROR 0本帧已交给 TCP 栈允许下一次触发计数加一ERROR 1发送失败读 STATUS 记录触发重发最多三次STATUS 7004仅连接建立不代表发送完成不能用来推进计数最后一条尤其重要编码的唯一性依赖「发成功才加一」如果把 7004 当成成功信号会出现一批标签打了同样的号或者计数器跑得比实际打印快。我现在的习惯是每套程序上线前先跑一遍「断网重连」测试——拔掉网线触发三次发送看计数器有没有异常递增再接回去看能不能自动恢复连接。从那以后我每次做打印相关的项目都会先拿 PC 直连打印机发一段最小指令验证硬件再进博图写逻辑这个顺序帮我省掉了太多在两边来回猜的时间。希望帮到你。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑