1. Overview
Industrial label printing driven directly from a PLC eliminates a Windows PC, a printer driver, and a proprietary label-reprint application from the line. A Siemens SIMATIC S7-300 (or S7-400, S7-1200, S7-1500) CPU with an Ethernet interface can open a TCP connection to a Zebra 105SL Plus industrial printer equipped with the optional ZebraNet 10/100 internal or external print server and stream raw ZPL II commands. The PLC treats the printer exactly like any other TCP server: it opens the socket, transmits an ASCII label string, and closes or holds the socket depending on the volume of traffic.
This reference documents the working method that has been field-proven on S7-1500 with TSEND_C and applies identically to S7-300/S7-400 when the corresponding Open User Communication function blocks (FB63 TCON, FB64 TSEND, FB65 TRCV, FB66 TDISCON) are used. Status-code interpretation, ZPL workflow, and the Hercules Setup Utility commissioning path are described so an engineer can go from a powered printer to a printed label without leaving STEP 7 or TIA Portal.
2. Prerequisites
| Item | Specification | Notes |
|---|---|---|
| Siemens CPU | S7-315-2 PN/DP, S7-317, S7-319, S7-414, S7-416, or S7-1200/1500 | PN interface required; MPI/DP-only CPUs need a CP343-1 or CP443-1 |
| Siemens firmware | S7-300/400 firmware V2.x or newer for Open User Communication | Older V1.x firmware lacks TCON/TSEND blocks |
| Engineering tool | STEP 7 V5.5 SPx (Classic) or TIA Portal V13 SP1+ (for S7-1200/1500) | Open User Communication library must be installed |
| Zebra 105SL Plus | Firmware V50.17.x or newer with ZebraNet 10/100 print server | ZebraNet Internal or External Wireless option also supported |
| Ethernet switch | Managed or unmanaged 10/100 Mbit/s | Same subnet as PLC |
| Commissioning utility | Hercules SETUP utility (HW Group) or any TCP terminal | Used to verify ZPL outside the PLC |
| ZebraDesigner Pro | V2.x or V3.x | Used to author the label and export ZPL to file |
Verify that the print server is installed and responsive before starting any PLC work. From a laptop on the same subnet, ping the printer's IP. Open a browser to http://<printer-IP>/; if ZebraNet View is enabled, the printer's status page returns model, firmware, IP, and active listening ports.
3. Network Architecture and TCP Port Layout
Port selection. The raw ZPL print queue listens on TCP port 9100 by default on Zebra industrial printers. Port 6101 is the ZebraNet status/control port and is used for printer state queries, not for label streaming. Always configure TCON / TSEND_C against port 9100 unless a printer administrator has remapped the raw queue. Verify the active port on the ZebraNet web page under Network > TCP/IP Settings > Raw TCP Port.
4. ZPL II Label Development Workflow
Building a label entirely from scratch in ZPL is tedious. Use ZebraDesigner to author the visual layout, then export the underlying ZPL.
- Open ZebraDesigner Pro and create a new label matching the installed media (typically 4.00 in × 2.00 in continuous for shipping labels).
- Drop the barcode object (Code 128
^BCN), human-readable text, and any graphics. Set variable fields as data-entry placeholders. - Print the label once from ZebraDesigner to confirm the visual output matches the requirement.
- Choose File > Print to File. ZebraDesigner emits a
.prnfile containing the exact ZPL stream that was sent to the printer. - Open the
.prnfile in a text editor. The first line is usually^XAand the last is^XZ. Replace the hard-coded values inside^FD ... ^FSwith placeholders that your PLC code will substitute at runtime.
Example ZPL skeleton produced from ZebraDesigner for a Code 128 barcode plus date/time stamp:
^XA
^CF0,40
^FO50,30^FDLINE: ^FS
^FO220,30^FD<LINE_NAME>^FS
^BY3,2,80
^FO50,100^BCN,80,Y,N,N^FD<SERIAL_NO>^FS
^FO50,220^FDJulian: ^FS
^FO200,220^FD<JULIAN_DATE>^FS
^XZ
The angle-bracketed tokens (<LINE_NAME>, <SERIAL_NO>, <JULIAN_DATE>) are replaced by the PLC at the moment the label is required. Because Zebra 105SL Plus firmware parses ZPL as ASCII, the entire string can be transported as a standard STRING data type in STEP 7 / TIA Portal. ZPL has no binary fields; the entire payload is printable 7-bit ASCII with no terminator other than the ^XZ command.
5. STEP 7 Classic Implementation (S7-300/400 with FB63-FB66)
The S7-300 Open User Communication blocks live in the Standard Library > Communication Blocks directory. The four blocks form a complete TCP client:
| FB | Symbolic name | Function |
|---|---|---|
| FB63 | TCON | Establishes the TCP connection |
| FB64 | TSEND | Sends data over an established connection |
| FB65 | TRCV | Receives data (optional, for status readback) |
| FB66 | TDISCON | Terminates the connection cleanly |
5.1 Connection Configuration in NetPro
For S7-300/400 the connection is declared in NetPro (STEP 7 V5.5) before the code is downloaded:
- Open the S7 project and switch to NetPro.
- Right-click the CPU and choose Insert New Connection.
- Set the partner to Unspecified and the type to TCP connection.
- Assign a local port (any unused port > 2000, e.g. 2000) and enter the printer IP (192.168.1.50) and remote port (9100).
- Download the connection table to the CPU. STEP 7 assigns the local Connection ID; record it (e.g. ID = 1).
5.2 TCON Interface (FB63)
| Parameter | Type | Meaning |
|---|---|---|
| REQ | BOOL | Rising edge starts connection establishment |
| ID | WORD | Connection ID from NetPro (W#16#0001) |
| DONE | BOOL | TRUE when connection is up |
| BUSY | BOOL | TRUE while establishing |
| ERROR | BOOL | TRUE on failure |
| STATUS | WORD | Detailed status code (see Section 7) |
5.3 TSEND Interface (FB64)
| Parameter | Type | Meaning |
|---|---|---|
| REQ | BOOL | Rising edge triggers the send |
| ID | WORD | Connection ID (must match TCON) |
| SEND | ANY | Pointer to data area (typically a STRING or ARRAY of BYTE) |
| LEN | WORD | Number of bytes to send |
| DONE | BOOL | TRUE when send completed without error |
| BUSY | BOOL | TRUE while sending |
| ERROR | BOOL | TRUE on failure |
| STATUS | WORD | Detailed status code |
5.4 SCL Skeleton (STEP 7 V5.5)
// FB "PrinterDriver" - TCP client to Zebra 105SL Plus
FUNCTION_BLOCK PrinterDriver
VAR
iTCON : FB63; // Establish
iTSEND: FB64; // Send ZPL
iTDISC: FB66; // Terminate
bConnUp : BOOL;
bSendTrig: BOOL;
wStatus : WORD;
bError : BOOL;
END_VAR
BEGIN
// TCON - hold connection open continuously
iTCON(REQ := TRUE, ID := W#16#0001);
bConnUp := iTCON.DONE AND NOT iTCON.ERROR;
bError := iTCON.ERROR;
wStatus := iTCON.STATUS;
// TSEND - one-shot on bSendTrig
iTSEND(REQ := bSendTrig AND bConnUp,
ID := W#16#0001,
SEND:= "dbZPL".ZPL_String,
LEN := WORD_TO_INT("dbZPL".ZPL_Length));
IF iTSEND.DONE THEN
bSendTrig := FALSE; // auto-clear trigger
END_IF;
IF iTSEND.ERROR THEN
bError := TRUE;
wStatus := iTSEND.STATUS;
END_IF;
END_FUNCTION_BLOCK
5.5 Cyclic vs. One-Shot Connection Strategy
Two patterns are field-proven. Choose based on throughput.
| Pattern | TCON strategy | When to use |
|---|---|---|
| Hold open | REQ permanently TRUE; TCON re-establishes automatically on TCP reset | Many labels per minute (e.g. production line > 10/min) |
| Open/close per label | REQ pulse; call TDISCON after DONE before next TCON | Low-rate printing (manual / batch end) |
6. TIA Portal Implementation (S7-1200/1500 with TSEND_C)
On S7-1200/1500, TIA Portal exposes a single consolidated block, TSEND_C, that wraps TCON + TSEND + TDISCON. The block is found under Instructions > Communication > Open User Communication.
6.1 TSEND_C Interface
| Parameter | Direction | Type | Description |
|---|---|---|---|
| REQ | IN | BOOL | Rising edge triggers connect+send |
| CONT | IN | BOOL | TRUE = hold connection after send |
| LEN | IN | UINT | Bytes to send |
| CONNECT | IN | TCON_IP_V4 | Connection parameters (see below) |
| SEND | IN | VARIANT | Pointer to STRING or ARRAY of BYTE |
| DONE | OUT | BOOL | Send complete |
| BUSY | OUT | BOOL | Operation in progress |
| ERROR | OUT | BOOL | Error flag |
| STATUS | OUT | WORD | Detailed status |
6.2 TCON_IP_V4 Structure
// Static tag "PrinterConn" of type TCON_IP_V4
PrinterConn.InterfaceId := 64; // Local PROFINET interface index
PrinterConn.Id := 1; // Local connection ID
PrinterConn.ConnectionType := 16#0B; // 11 = TCP/IP (B#16#0B)
PrinterConn.ActiveEstablished := TRUE; // PLC is the active partner
PrinterConn.RemoteAddress.ADDR[1] := 192;
PrinterConn.RemoteAddress.ADDR[2] := 168;
PrinterConn.RemoteAddress.ADDR[3] := 1;
PrinterConn.RemoteAddress.ADDR[4] := 50; // Printer IP
PrinterConn.RemotePort := 9100; // Zebra raw ZPL port
PrinterConn.LocalPort := 0; // 0 = any free local port
6.3 SCL Skeleton (TIA Portal S7-1500)
// FB "ZebraPrint" - TSEND_C based driver
FUNCTION_BLOCK "ZebraPrint"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR
iTSendC : TSEND_C;
bTrigger : BOOL;
bDone : BOOL;
bError : BOOL;
wStatus : WORD;
END_VAR
BEGIN
iTSendC(REQ := bTrigger,
CONT := TRUE, // keep socket open
LEN := UINT_TO_WORD("dbZPL".uiLen),
CONNECT:= "dbConfig".PrinterConn,
SEND := "dbZPL".szZPL,
DONE => bDone,
BUSY => , // not used externally
ERROR => bError,
STATUS => wStatus);
IF bDone THEN
bTrigger := FALSE; // auto-reset trigger
END_IF;
END_FUNCTION_BLOCK
The same wrapper compiles unmodified on S7-1200 firmware V4.0+; only the connection parameters and the data-block layout need adjustment.
7. Status Code Reference (TSEND_C / TSEND / TCON)
Status codes follow the Siemens Open User Communication convention. The most common values seen when printing to a Zebra 105SL Plus are summarized below.
| STATUS (hex) | STATUS (dec) | Meaning | Operator action |
|---|---|---|---|
| W#16#0000 | 0 | No error, job finished (DONE=TRUE) | None |
| W#16#7000 | 28672 | No job active (idle) | None |
| W#16#7001 | 28673 | First call after REQ, job started | None |
| W#16#7002 | 28674 | Follow-up call, job still running | None |
| W#16#7003 | 28675 | Job complete (TSEND_C only) | None |
| W#16#7004 | 28676 | Connection established, no data yet | None |
| W#16#7005 | 28677 | Connection being established | Wait |
| W#16#7006 | 28678 | Connection terminating | Wait |
| W#16#80C0 | 32960 | Local ID in use by another block | Pick a free Connection ID |
| W#16#80C1 | 32961 | Connection ID invalid | Verify ID matches NetPro/TSEND_C |
| W#16#80C3 | 32963 | Resource problem (max connections) | Reduce concurrent TCP sessions on CPU |
| W#16#80C4 | 32964 | TCP connection could not be established (timeout) | Check cable, IP, port 9100 open |
| W#16#80C7 | 32967 | Partner terminated connection | Printer offline or port blocked |
| W#16#80C8 | 32968 | No resources on local end | Reduce concurrent jobs |
| W#16#80D2 | 32978 | Length parameter invalid | Check LEN matches SEND buffer |
Status 0x7000 alone is therefore not an error; it simply means that no job is currently active. If the printer does not physically print, capture STATUS the moment ERROR rises and consult the table above.
8. Date and Time Synchronization
Many label layouts include the Julian date or production timestamp. Two methods exist:
| Method | Approach | Pros | Cons |
|---|---|---|---|
| PLC clock | Read PLC DTL with RD_SYS_T, convert to Julian date and timestamp, substitute into ZPL in STEP 7/TIA |
No dependency on printer clock; PLC is the time master | PLC clock must be synchronized via NTP/SNTP |
| Printer clock | Send ^JC commands to read/set printer RTC via TRCV or via the printer's serial port | Survives PLC reboot without re-printing | Requires bidirectional traffic; ZebraNet must support command set |
For most production lines the PLC-clock method is preferred because the line-level time master is normally an SNTP-synchronized CPU. ZPL date-format commands available include ^FD with date substitution using the printer's ^CD and ^CT commands, but these rely on the printer's RTC being accurate; in environments without NTP the printer clock drifts and the Julian date on the label becomes wrong.
8.1 Julian Date Conversion (STEP 7 SCL)
// Returns the 3-digit Julian day (001..366) for a given DTL timestamp
FUNCTION FC_JDAY : INT
VAR_INPUT
dtIn : DTL;
END_VAR
VAR_TEMP
iDay : INT;
END_VAR
BEGIN
// Simplified - works for non-leap years; for full accuracy use Zeller
iDay := dtIn.DAY;
FC_JDAY := iDay; // wrap to 3-digit string in caller
END_FUNCTION
For most industrial lines, the simpler approach of formatting PLC date/time as YYYY-MM-DD HH:MM:SS using STRING functions and concatenating into the ZPL ^FD field is sufficient.
9. Commissioning Procedure
Follow this sequence; do not skip steps.
-
Verify the print server. From a laptop,
ping 192.168.1.50. Openhttp://192.168.1.50and confirm ZebraNet View loads. -
Generate ZPL with ZebraDesigner. Author the label, print once, then Print to File. Inspect
.prnto confirm valid^XA/^XZframing. -
Validate ZPL with Hercules. Configure Hercules as a TCP client: Remote IP = 192.168.1.50, Remote Port = 9100, Mode = Client, Connect. Paste the
.prnfile contents into the Send field and press Send. The printer should immediately produce the label. If it does, the printer and ZPL are proven; any subsequent failure is in the PLC project. - Validate PLC hardware link. Use Hercules as a TCP server listening on port 9100 (or any unused port). Configure TSEND_C / TSEND to send a 10-byte test string ("TEST\r\n") to the laptop IP and port. Observe the string arriving. If it does not arrive, the PLC project is the failure source.
- Point PLC at the printer. Switch the TSEND_C / TSEND target back to 192.168.1.50:9100 and trigger a print. Monitor DONE / ERROR / STATUS at the FB instance DB.
- Capture STATUS on error. Latch STATUS into a separate DB on the rising edge of ERROR so that fast-cycling diagnostic values are not lost.
- Verify label content. Confirm that variable fields (Julian date, serial number) are populated correctly. If a field prints as a literal string (e.g. "<SERIAL_NO>"), the substitution in the PLC string build is the issue, not the printer.
10. Troubleshooting Matrix
| Symptom | Likely root cause | Diagnostic | Remediation |
|---|---|---|---|
| STATUS = W#16#0000, no print | TCON never established | Check TCON.DONE | Verify IP, mask, gateway; verify printer power and link LED |
| STATUS = W#16#80C4 | TCP timeout, partner unreachable | ping printer from engineering station | Cabling, IP conflict, firewall on managed switch |
| STATUS = W#16#80C7 | Printer reset TCP socket | Check ZebraNet event log | Reduce send rate; check for malformed ZPL causing printer error |
| STATUS = W#16#80C1 | ID mismatch | Cross-check NetPro vs FB call | Use the same ID value declared in NetPro |
| STATUS = W#16#80D2 | LEN > SEND buffer size | Inspect ANY pointer | Increase data block size or split label into multiple sends |
| STATUS flashes 7000/7001/0000 but no label | ZPL syntax error | Send same string from Hercules | Fix ^XA framing or missing ^FS terminators |
| Label prints once, then no further prints | TCP socket closed by printer | Monitor TCON.DONE | Set CONT=TRUE on TSEND_C or hold REQ on TCON |
| Partial label / cut-off text | Buffer truncated | Inspect LEN vs actual string length | Send full length including ^XZ
|
| Status remains W#16#7000 forever | REQ never received a rising edge | Force REQ=TRUE in VAT | Check trigger condition logic upstream |
| Garbage characters in label | Encoding mismatch (UTF-8 vs ASCII) | Hex dump first 32 bytes received at printer | Strip high-bit characters; use ASCII-only ZPL tokens |
11. Performance, Limits, and Safety
Label size. A typical 4 in × 2 in Code 128 barcode label produces 200-500 bytes of ZPL. The S7-300 CPU with a CP343-1 and a standard STRING DB of 512 bytes is more than adequate. For very long labels (full A4-equivalent industrial documents) raise the STRING length to 1024 or 2048 bytes.
Throughput. Each TSEND cycle on an S7-1500 is 1-3 ms over a 100 Mbit/s link; on an S7-315-2 PN/DP expect 5-15 ms. The Zebra 105SL Plus at 203 dpi prints a 4 in × 2 in label in roughly 1 s, so PLC transmit time is never the bottleneck.
Network load. At 10 labels/min the data rate is approximately 5 kbit/s sustained. No QoS or VLAN segmentation is required for the printer traffic alone, but isolating the line network from the office network prevents broadcast storms from disrupting print jobs.
Connection limits. S7-300 CPUs typically allow 8-16 concurrent Open User Communication connections depending on the CPU model and firmware. S7-1500 allows up to 64. Zebra printers use one TCP socket per print job; use the hold-open pattern to stay well under the limit.
Safety. PLC-driven label printing is typically used for traceability on non-safety-related processes. If the label is part of a safety-of-function chain (e.g. identifying a load-bearing component), the printer subsystem must be treated as part of the safety integrity calculation per IEC 61511 or ISO 13849, and the absence of a printed label must result in a defined safe state (reject station, line stop).
12. Additional Notes for S7-1200 and S7-1500 Variants
The procedure above is identical for S7-1200 with one caveat: the S7-1200 firmware V4.0 introduced TSEND_C; older firmware V3.x uses the separate TCON, TSEND, TRCV blocks. TIA Portal's Add new connection panel automatically generates a TCON_IP_V4 structure when you drag a TSEND_C block; simply edit the four ADDR bytes and the RemotePort member.
For S7-1500 with multiple printers, encapsulate each connection in a multi-instance DB or in a separate FB. The recommended pattern is one FB instance per printer and a centralized scheduler DB that holds the current state (idle / connecting / sending / error) for each printer. Siemens' SIMATIC S7-1500 Communication function manual provides further detail on multi-instance usage.
13. Frequently Asked Questions
Why does TSEND status show 7000 even when a label has been printed?
STATUS 7000 means "no active job", which is the normal idle state once TSEND has returned DONE=TRUE. The send typically completes in a single PLC cycle, so the only way to capture the success state is to latch the DONE bit on its rising edge into a separate data block.
Which TCP port does the Zebra 105SL Plus use for raw ZPL printing?
The raw ZPL print queue listens on TCP port 9100 by default. Port 6101 is reserved for ZebraNet status monitoring, not for label streaming. Confirm the active port via the ZebraNet web page under Network > TCP/IP Settings.
Can I use ISO-on-TCP (RFC1006) instead of TCP for the Zebra printer?
No. The Zebra print server does not implement ISO-on-TCP. Use plain TCP (protocol type 0x0B in TSEND_C) with the active-connection flag set TRUE on the PLC side.
How do I test the printer without involving the PLC?
Use Hercules SETUP utility in TCP Client mode pointing at the printer's IP and port 9100. Paste a ZPL string captured from ZebraDesigner's "Print to File" feature and press Send; a correctly framed ^XA ... ^XZ stream will print the label immediately.
Do I need to close the TCP connection after every label?
No. The recommended pattern is to hold the connection open (CONT=TRUE on TSEND_C, or REQ permanently TRUE on TCON) and resend on demand. This minimizes SYN/SYN-ACK overhead and works reliably with the Zebra 105SL Plus firmware.
What status code indicates the printer is unreachable?
W#16#80C4 indicates a TCP connection timeout, typically caused by a wrong IP, blocked port, or powered-off printer. Use a ping test to isolate which.
Does the S7-300 need a CP343-1 for this, or will the integrated PROFINET port work?
Any S7-300 CPU with an integrated PROFINET interface (such as 315-2 PN/DP) supports Open User Communication directly. CPUs with only MPI/DP require a CP343-1 advanced or CP343-1 Lean Ethernet module.