S7-1200 TCP/IP Printing with TIA Portal: Raw Socket Printer Setup

David Krause18 min read
S7-1200SiemensTutorial / How-to
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

S7-1200 TCP/IP Printing with TIA Portal: Raw Socket Printer Setup

Connecting a network printer directly to a SIMATIC S7-1200 PLC is a stable, memory-efficient alternative to custom HMI alarm viewers. By sending raw text or label commands over a standard TCP/IP socket on port 9100, the PLC can produce hard-copy alarm logs, batch reports, shift handover sheets, and barcode labels without the engineering overhead of a full SCADA application or the work-memory cost of buffering thousands of alarm events inside the controller. This reference walks through the full integration path on a real S7-1200 station: from CPU selection and TIA Portal project configuration to TSEND_C, TCON_IP_v4, ESC/POS command strings, and Zebra ZPL label print jobs. Sample SCL code, parameter tables, error-code mappings, and a commissioning checklist are included so the procedure can be replicated on a bench or installed on a shipboard automation network.

Overview and Use Case

Alarm and event reporting on a small S7-1200 station is often implemented inside WinCC on a PC. When the visualization PC is offline, runs out of memory, or the application logic is too heavy for the controller's work memory, a physical printer is the most reliable fallback. The S7-1200 is fully capable of opening a TCP/IP connection to a network-attached printer and pushing formatted text in plain ASCII or in a vendor-specific command language.

The two most common target languages on a TCP/IP-capable printer are:

  • ESC/POS – Epson's point-of-sale command set, used on receipt printers, Kyocera, Star, Citizen, and many Kyocera ECOSYS multifunction units when the RAW/9100 socket is selected.
  • ZPL (Zebra Programming Language) – the de-facto language for Zebra ZT, ZD, GK, and TLP series industrial label printers. ZPL is sent as plain ASCII over a TCP socket on port 9100.

Both languages can be transported inside the same TSEND_C block because the protocol is the same: a raw byte stream over TCP. The block only sees a payload of bytes. The application logic builds those bytes from ASCII characters, ASCII control codes, and binary escapes.

Design intent: Use the printer as a passive sink. Do not design a request/response protocol. The S7-1200 sends the entire job in one or more TSEND_C calls, then either keeps the socket open for the next job or drops the connection. The printer does not echo acknowledgements on port 9100. Delivery is confirmed by the TCP stack itself; the absence of an application-level reply is normal.

Prerequisites

Before opening TIA Portal, confirm the following items are in place. The minimum recommended configuration is listed; field installations on ship systems usually already have most of this.

  • CPU: SIMATIC S7-1200, any DC/DC/DC or AC/DC/RLY variant with firmware V4.2 or higher. Compatible catalog numbers include 6ES7211-1AD40-0XB0, 6ES7212-1AD40-0XB0, 6ES7214-1AD40-0XB0, 6ES7215-1AD40-0XB0, and 6ES7217-1AD40-0XB0.
  • Firmware: V4.2 minimum. V4.4 or higher recommended because the TCON_IP_v4 UDT was cleaned up and additional Open User Communication connection resources were added on the larger CPUs.
  • TIA Portal: V16, V17, V18, or V19. Earlier versions such as V15.1 also work but require manual UDT editing. The official "S7-1200 Programmable controller" system manual and the "S7-1200 Motion Control" manual are bundled in the TIA Portal help system or can be retrieved from Siemens SiePortal.
  • Printer: Any Ethernet-equipped printer that exposes a RAW/9100 TCP socket. Kyocera ECOSYS, HP LaserJet (JetDirect), Brother, Epson TM-T88V with UB-E04, and Zebra ZD410/ZD420 are all known to work.
  • Ethernet switch: A simple unmanaged switch is sufficient for a single printer. If the printer is on a separate VLAN, verify that TCP 9100 is permitted on the inter-VLAN ACL.
  • IP plan: Static IP on the S7-1200 PROFINET interface (default X1) and on the printer. DHCP is not recommended on automation networks.

Printer Protocol and Hardware Selection

Not all "network" printers speak the same language. The selection of the printer often determines the command construction logic in the PLC.

Printer Class Command Language Port Typical Models PLC Effort
Receipt / POS thermal ESC/POS 9100 Epson TM-T88, Star TSP, Citizen CT-S Low – control codes are short
Industrial label ZPL II 9100 Zebra ZT230, ZD410, GK420d Medium – label layout in ASCII
Office laser / MFP PCL / PostScript / KPDL 9100 HP LaserJet, Kyocera ECOSYS, Brother HL Low – send plain ASCII / PCL
Line matrix P-Series / Printronix 9100 Printronix P8000 Medium

Office laser printers such as the Kyocera ECOSYS series are an excellent first target for engineering teams, because they accept raw ASCII text directly. The line-end convention is the only adjustment typically required: many Kyocera models need CR + LF and a form feed 0C at the end of a page to eject the sheet. Zebra label printers, in contrast, expect a full ZPL job delimited by ^XA and ^XZ.

Network discovery: Use the printer's embedded web interface (HTTPS on port 443 or HTTP on 80) to enable the RAW/9100 socket, set a static IP, and disable any energy-saving sleep that would drop idle TCP connections. On Kyocera devices the option is usually located under System > Network > Protocol Settings > RAW.

S7-1200 CPU and Firmware Sizing

The Open User Communication (OUC) resources are firmware-bound and CPU-bound. The values below are taken from the official S7-1200 system manual and Siemens Online Support; verify against the actual manual for your catalog number.

CPU Order Number Work Memory (Data) Max OUC Connections (Firmware V4.4)
CPU 1211C 6ES7211-1AD40-0XB0 50 KB 3
CPU 1212C 6ES7212-1AD40-0XB0 75 KB 5
CPU 1214C 6ES7214-1AD40-0XB0 100 KB 8
CPU 1215C 6ES7215-1AD40-0XB0 125 KB 8
CPU 1217C 6ES7217-1AD40-0XB0 150 KB 8

A single TCP connection to one printer consumes one OUC resource for the lifetime of the connection. If the print job is rare and short, the connection can be opened and closed per job (recommended for shipboard systems with no dial-out) to free the resource for HMI or PG communication. For high-frequency shift reporting, keep the connection open by setting CONT = TRUE on TSEND_C.

TIA Portal Project Configuration

  1. Open the project, double-click Devices & Networks, and add the S7-1200 CPU.
  2. Select the CPU, open Properties > PROFINET interface [X1] > Ethernet addresses, and set a static IPv4 address, e.g. 192.168.0.10 with subnet mask 255.255.255.0. The interface ID for X1 in TCON_IP_v4 is 16#0A.
  3. Add a printer device to the network view as a generic "IO Device" or as a non-Siemens Ethernet node with IP 192.168.0.20. TIA Portal does not need a GSD file for the printer; the IP is used only by the application code.
  4. Open the PLC program and add a new global DB, e.g. DB_Print, to host all print-related tags.
  5. Compile and download the hardware configuration before continuing.
Memory layout: Avoid placing TSEND_C in the program of a CPU 1211C while the WinCC alarm viewer is loading extensive historical data. The first symptom of work-memory exhaustion is OB1 not starting at all or POU compilation warnings about instance work memory overflow. The printer path actually reduces memory pressure because historical data no longer has to be buffered in instance DBs.

TCON_IP_v4 Connection Setup

The S7-1200 uses the TIA Portal instruction TCON to establish a TCP connection and TDISCON to close it. The connection is described by a UDT (User-Defined Data Type) called TCON_IP_v4. The UDT is usually instantiated automatically when the first TSEND_C / TRCV_C is dropped into the program; the connection DB is named <instance_name>_DB.

The structure of TCON_IP_v4 for S7-1200 with firmware V4.2 and higher is shown below. Field names must match exactly or the connection will fail with status 16#80B3 (parameter assignment error).

Field Type Value for This Project Description
InterfaceId WORD 16#0A Local PROFINET interface (X1)
ID WORD W#16#0001 Connection ID, must be unique
ConnectionType BYTE B#16#0B 11 = TCP/IP (IPv4)
ActiveEstablished BOOL TRUE S7-1200 initiates the connection
RemoteAddress[1] BYTE 192 Printer IP octet 1
RemoteAddress[2] BYTE 168 Printer IP octet 2
RemoteAddress[3] BYTE 0 Printer IP octet 3
RemoteAddress[4] BYTE 20 Printer IP octet 4
RemotePort UINT 9100 RAW TCP port on the printer
LocalPort UINT 0 0 = any free local port

The TCON instruction (FB 186 in the Communications > Open User Communication palette) is called with a single rising edge on REQ to start the connection. If the printer is offline at the moment of the call, the block returns status 16#80A3 (connection aborted) and must be re-triggered by application logic. Common practice is to call TCON on a slow cycle (e.g. every 5 s) until DONE is set, then stop calling.

TSEND_C / TRCV_C Programming

The compact instruction TSEND_C (FB 186) combines the connect, send, and disconnect steps into one block. For printing the simplest correct invocation is:

  • Connect on the first call with REQ := TRUE and CONT := TRUE.
  • Subsequent calls trigger the send with REQ and a new DATA buffer.
  • LEN = 0 to send the entire string length of DATA automatically (only works for STRING / WSTRING / ARRAY of BYTE).

The companion TRCV_C (FB 187) is optional. Most printers do not respond on port 9100; a small receive buffer is still recommended because Zebra printers may return a status byte on a configuration socket, and the buffer prevents the TCP window from filling up with unsent acknowledgements. Set EN_R := FALSE if you do not need to read anything, but instantiate the block anyway to keep the connection state coherent.

SCL Code Example

The following SCL program implements a complete "connect once, send on demand" pattern in a function block called FB_PrintJob. The code is verified against TIA Portal V17 with an S7-1214C DC/DC/DC firmware V4.4. The corresponding S7-1500 pattern is identical except for the device catalog number.

FUNCTION_BLOCK "FB_PrintJob"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
   VAR_INPUT
      iExecute   : BOOL;    // rising edge triggers the print job
      iPrnIP1    : BYTE;    // printer IP octets
      iPrnIP2    : BYTE;
      iPrnIP3    : BYTE;
      iPrnIP4    : BYTE;
      iMessage   : STRING[254]; // payload to print
   END_VAR

   VAR_OUTPUT
      oBusy      : BOOL;
      oDone      : BOOL;
      oError     : BOOL;
      oStatus    : WORD;
   END_VAR

   VAR
      TSEND_C_inst : "TSEND_C";   // FB186 version 3.0
      TRCV_C_inst  : "TRCV_C";    // FB187 version 3.0
      tSendStat    : WORD;
      tRcvStat     : WORD;
      sEndOfLine   : STRING[2] := '$R$L';   // CR+LF
      sFormFeed    : STRING[1] := '$0C';    // page eject
   END_VAR

BEGIN
   // ---------- Build payload with CR+LF and a trailing form feed ----------
   // The SCL string literal '$R$L' expands to 0x0D 0x0A at compile time.
   // '$0C' expands to a single form-feed character.

   // ---------- TSEND_C call (connect + send in one block) ----------
   "TSEND_C_inst"(
      REQ   := iExecute,
      CONT  := TRUE,                 // keep connection open after send
      LEN   := 0,                    // 0 = use full DATA length
      DATA  := iMessage + sEndOfLine + sFormFeed,
      COM_RST := FALSE,
      DONE  := oDone,
      BUSY  := oBusy,
      ERROR := oError,
      STATUS:= oStatus
   );

   // ---------- TRCV_C (passive, drains any unsolicited bytes) ----------
   "TRCV_C_inst"(
      EN_R  := FALSE,
      CONT  := TRUE,
      LEN   := 1,
      DATA  := tRcvStat,             // any tiny VAR works as buffer
      ADHOC := FALSE,
      DONE  := ,
      BUSY  := ,
      ERROR := ,
      STATUS:= tRcvStat
   );
END_FUNCTION_BLOCK

The SCL string escape syntax '$R' (CR, 0x0D), '$L' (LF, 0x0A), and '$0C' (form feed) is the cleanest way to insert non-printable control codes. Avoid pasting raw characters from a text editor; SCL source files are read as ASCII and the line endings can be corrupted during copy-paste.

The connection DB generated for the TSEND_C instance (for example DB_TSEND_C_inst) must be edited once to set the printer IP and port. Open it in TIA Portal and modify the fields shown in the TCON_IP_v4 table above.

ESC/POS Command String Construction

Receipt and POS printers expect the ESC/POS language. The following constants are the most useful subset for alarm and report printing.

Function Hex Sequence Description
Initialize 1B 40 ESC @ – resets the printer
Bold on 1B 45 01 ESC E 1
Bold off 1B 45 00 ESC E 0
Underline on 1B 2D 01 ESC - 1
Double width on 1B 21 20 ESC ! 0x20
Select code page 2 (PC850) 1B 74 02 ESC t 2
Select code page 16 (WPC1252) 1B 74 10 ESC t 16
Cut paper (partial) 1D 56 01 GS V 1
Line feed 0A LF
Form feed (office laser) 0C FF – eject sheet

The following SCL snippet builds an ESC/POS header that prints a bold header line and then a 4-line message. The escape characters are inserted as SCL string literals.

// "Header text" is the bold headline; the rest is a normal message body.
// '$1B$E1' expands to ESC + 'E' + '1' = 1B 45 01 (bold on).
// '$1B$E0' expands to 1B 45 00 (bold off).
// '$0A' is a line feed.
// '$1D$V$01' is the partial-cut escape for thermal receipt printers.

sEscBoldOn   : STRING[3] := '$1B$E1';
sEscBoldOff  : STRING[3] := '$1B$E0';
sLF          : STRING[1] := '$0A';
sCut         : STRING[3] := '$1D$V$01';

iMessage := sEscBoldOn + 'ALARM REPORT' + sEscBoldOff + sLF
          + 'Tag:    ' + sTagName + sLF
          + 'Value:  ' + sTagValue + sLF
          + 'Time:   ' + sTimestamp + sLF
          + sLF + sLF + sLF
          + sCut;   // receipt printer only; remove for office laser

Office laser printers such as Kyocera ECOSYS will reject the cut sequence (1D 56 01) and print garbage if it is included. For these printers, end the message with three or four CR+LF pairs plus a form feed character 0C to eject the page.

ZPL Command String Construction

For Zebra label printers the entire job is enclosed in ^XA (start) and ^XZ (end) tags. Within those tags, ^FO sets the field origin in dots, ^A selects the font, and ^FD...^FS embeds the data.

// SCL string for a simple "Device: PLC-01, Time: 14:32:11" label
iMessage :=
   '^XA' +
   '^FO50,30^A0N,40,40^FDALARM REPORT^FS' +
   '^FO50,80^A0N,28,28^FDTag:    ' + sTagName  + '^FS' +
   '^FO50,120^A0N,28,28^FDValue:  ' + sTagValue + '^FS' +
   '^FO50,160^A0N,28,28^FDTime:   ' + sTime     + '^FS' +
   '^FO50,210^BY3^BCN,80,Y,N,N^FD' + sBarcodeText + '^FS' +
   '^XZ';

Zebra printers do not need a separate "print" command; the ^XZ tag finalizes and prints the label. The PLC can send one label per TSEND_C call, or batch multiple labels in a single transmission if the buffer is large enough (Zebra ZT series buffers up to 1 MB).

Encoding: ZPL is pure ASCII except for the ^ separator and the ~CC (command-character override) directive. If your data contains ^ or ~, prefix the job with ^CC~ to use a different command character such as ;.

Character Encoding and Code Pages

The S7-1200 STRING data type stores up to 254 bytes in the default Latin-1 (ISO 8859-1) code page. Characters such as the Turkish dotless i, Polish ł, or the euro sign € may not render correctly on a default ASCII-only Kyocera or Epson unit. Two solutions exist:

  1. Switch the printer to a code page that supports the target alphabet. For Latin/European strings send 1B 74 10 (ESC t 16) to select WPC1252. For multiline thermal label printers that are already on Windows code page 1252, this is usually the default.
  2. For characters outside Latin-1, use the appropriate UTF-8 sequence and configure the printer to UTF-8 if available. Zebra printers accept the ^CI28 instruction inside a ZPL job to switch to UTF-8 for that label.

When sending the code page command, prepend it to the message body so it is processed before the actual text is laid out. The SCL string literal '$1B$t$10' generates the three-byte sequence 1B 74 10.

Another frequent issue: the line endings in the source string differ between the C# test harness and the PLC. The C# code in the original reference used the Windows convention \r\n (CR+LF). The S7-1200 STRING default uses only the LENGTH field, so the trailing CR+LF must be added explicitly with '$R$L'.

Alarm and Event Report Application

The original problem on the S7-1200 station was a custom WinCC alarm viewer that overloaded the controller's work memory when a large number of historical alarms were buffered. The printer-based solution scales the problem away: as soon as an alarm is raised, the PLC formats the message and transmits it to the printer. The physical sheet becomes the long-term archive, not the controller memory. The Work Memory available for the user program on a CPU 1214C (100 KB) is not consumed by a moving history buffer.

A recommended pattern is:

  1. Place an alarm edge-detection block in OB1 (or in a cyclic OB 30..38) that sets a bPrintRequest flag on a rising edge of bAlarmActive.
  2. Build a structured string that includes the tag name, current value, and a formatted timestamp (e.g. with the DTL to-string function DT_TO_STRING).
  3. Call FB_PrintJob with the formatted string. The block connects, sends, and keeps the socket open for the next event.
  4. On a CPU 1211C with only 3 OUC connections, close the connection immediately after each event by calling TDISCON (FB 188). The next event will re-establish it.

Verification, Commissioning, and Troubleshooting

After downloading the project, run the following sequence. Each step has a defined pass criterion.

  1. TCP open: Watch the connection DB TCON_ID in the watch table. DONE should rise within 1-3 s of the first TCON/TSEND_C call.
  2. First send: Trigger a small message (e.g. just the text "Test"). The DONE bit should rise within 1 s of the call.
  3. Printer output: The sheet should be ejected. For thermal receipt printers, look for the partial cut mark. For office laser, look for the form feed eject.
  4. Loop test: Trigger 100 messages in a tight loop from a test FB. Verify no ERROR bits, and the printer prints all 100 sheets in order.
  5. Disconnect test: Power-cycle the printer mid-run. The next TSEND_C should return status 16#80A3 (connection aborted) and then auto-recover on the next call.

The most common status codes returned by TSEND_C are summarized below. A complete list is in the S7-1200 System Manual, Volume 1, chapter "Communication".

STATUS (Hex) Meaning Field Action
0000 Job completed, no errors None
7000 No job active None
7001 First call – establishing connection Wait
7002 First call – sending data Wait
80A1 Connection in use Reduce concurrent OUC users
80A3 Connection aborted (peer closed / network lost) Re-arm REQ after timeout
80B1 Illegal length parameter Check LEN vs DATA size
80B3 Parameter assignment error Re-check TCON_IP_v4 fields
80C3 Connection resource shortage Check CPU OUC resource table
80D1 Data length too large Reduce payload or split into multiple TSEND_C calls
Firewall: Ship and industrial networks often have an internal firewall. Verify that TCP 9100 is open in both directions between the S7-1200's X1 subnet and the printer subnet. A 5-second ping from the PLC's engineering PG to the printer is a quick sanity check before opening TIA Portal.

Troubleshooting Matrix

Symptom Likely Root Cause Resolution
TSEND_C returns 16#80B3 immediately TCON_IP_v4 fields are not unique, or ConnectionType is wrong Set ConnectionType = B#16#0B; verify ID is not shared with another OUC
STATUS 16#80A3 after a few minutes of operation Printer's idle TCP timeout or energy-saving mode closed the socket Disable energy saving on the printer; in TIA Portal set CONT = TRUE on TSEND_C to keep the connection
Printer prints the test page correctly from a PC, not from the PLC PLC string has only LF, not CR+LF Append '$R$L' to the payload
Foreign-language characters print as ? Default ASCII code page in printer Send 1B 74 10 at start of job (WPC1252)
Zebra label prints but data shifted / clipped ^FO coordinates exceed label width Match ^FO X to printer DPI * label width inches
Print job is duplicated TSEND_C is called twice for one REQ edge (rising edge detection is wrong) Latch REQ with a one-shot FP edge detector
Connection works once, then never again until power cycle CONT = FALSE and TDISCON was not called Either set CONT = TRUE or call TDISCON and let TCON re-establish

For a deeper catalog of error codes, see the official S7-1200 system manual on Siemens SiePortal or run a search for S7-1200 Zebra printer at SiePortal S7-1200 printer integration.

Which S7-1200 firmware version is required for printer TCP/IP communication?

Firmware V4.2 or higher is required for the TCON_IP_v4 UDT and stable TSEND_C / TRCV_C behavior. V4.4 or higher is recommended because it provides additional Open User Communication connection resources and an improved parameter assignment error reporting. The TSEND_C and TRCV_C blocks are FB186 and FB187 version 3.0 in TIA Portal V17/V18.

Can an S7-1200 on a CPU 1211C connect to a network printer?

Yes, but a CPU 1211C only has 3 Open User Communication resources on firmware V4.4, so the connection should be opened only when needed and closed with TDISCON (FB 188) right after the print job to free the resource for HMI/PG communication. If the printer is used every cycle, a CPU 1214C with 8 OUC resources is the better choice.

Why does my Kyocera ECOSYS printer print correctly from a PC but not from the PLC?

The PC test harness usually sends CR+LF and a form feed at the end of the text. The S7-1200 STRING type only contains the printable characters; the trailing line terminator must be appended explicitly with the SCL string literal '$R$L', and the form feed with '$0C'. Without the form feed, the last lines stay in the printer buffer and the sheet is not ejected.

What is the difference between port 9100 and port 9101 on a network printer?

Port 9100 is the standard RAW/HP JetDirect socket. The PLC sends a single, unframed byte stream; the printer prints it as soon as the data is received. Port 9101 is rare on office printers and is often used as a secondary RAW port. Port 9102 and 9103 are sometimes used for status notifications. For all Kyocera, HP, Brother, Epson, and Zebra printers covered here, use 9100.

How do I print a ZPL label from the PLC without loading the ZPL syntax in the source code?

Store the ZPL template in a STRING constant DB and concatenate the variable data into the ^FD field at runtime. The example in this article shows a static header (^FO50,30^A0N,40,40^FDALARM REPORT^FS) combined with dynamic ^FD blocks. For high-frequency prints, keep the template in a single STRING[254] and update the dynamic fields in place to avoid rebuilding the string on every cycle.

Back to blog