Converting S7-300 CHAR Array to STRING for WinCC Flexible HMI

David Krause18 min read
S7-300SiemensTutorial / 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

Problem Overview and Use Case

A CP341 point-to-point module on a SIMATIC S7-300 station receives decoded barcode strings from a SICK CLV430 line scanner. The standard loadable driver (ASCII or 3964R) places the payload into a destination data block declared in the instance call of FB2/FB3. The same is true for the RK512 driver through FB6/FB7 (P_SND_RK / P_RCV_RK) used in the original project. In all cases the data lands as a flat ARRAY OF CHAR and not as a STRING. WinCC Flexible 2008 does not expose a CHAR-array tag on an I/O field or output field; the only character data type that the runtime can display on a TP177A panel is STRING. Without conversion the HMI screen renders nothing, garbled characters, or stale data from the previous read.

The same conversion problem appears for any ASCII source feeding an S7-300 through a CP340 or CP341, including weigh scales, RFID heads, vision systems, and third-party serial devices. The fix is always the same: produce a properly formatted STRING tag and bind it to an HMI variable. This article documents three implementation methods, a verification procedure, and a fault-finding matrix for the CP341 + SICK CLV430 + WinCC Flexible 2008 + TP177A stack on STEP 7 V5.5.

Hardware and Software Environment

Item Model / Article Number Role
CPU SIMATIC S7-300 CPU 314C-2DP (6ES7314-6CG03-0AB0 or later 6ES7314-6CH04-0AB0) Compact PLC with integrated I/O and PROFIBUS DP master, 192 KB work memory, 24 DI / 16 DO integrated
Serial module CP341 (6ES7341-1AH01-0AE0 or 6ES7341-1AH02-0AE0 for RS-232C; 6ES7341-1BH02-0AE0 for RS-422/485) Point-to-point communication; one channel per module
Barcode scanner SICK CLV430 laser line scanner Serial RS-232/422 output, SOPAS configurable
HMI panel SIMATIC TP177A 6" touch, mono (6AV6 641-0AA11-0AX0) or color (6AV6 641-0BA11-0AX0) Operator panel, 320 x 240 pixels
PLC engineering STEP 7 V5.5 with SP2 or later SIMATIC Manager project
CP341 parameterization PtP drivers on the CP341 parameterization disk (ASCII, 3964R, RK512) Loadable protocol drivers
HMI engineering WinCC Flexible 2008 SP3 or later TP177A image and tag editor

References for the components above are published in the Siemens Industry Online Support portal and on the SICK AG product pages.

Protocol selection: The SICK CLV430 ships from the factory with the "SICK Standard" or generic ASCII output format over RS-232/422 at 9600 baud, 8 data bits, no parity, 1 stop bit, and a CR/LF terminator. The CP341 ASCII driver with FB2 P_SND and FB3 P_RCV is the natural match. The original project uses FB7 P_RCV_RK from the RK512 driver; if you keep that selection the scanner must be re-configured in SOPAS to send RK512 telegrams, otherwise the CP341 will report framing errors. Confirm the protocol on the scanner before touching the PLC code.

Siemens STRING Data Type Internals

The S7-300 STRING is a STRUCT with a fixed two-byte header followed by a character array:

TYPE STRING
   STRUCT
      maxLen   : BYTE;                        // Byte 0: maximum length (typ. 254)
      actLen   : BYTE;                        // Byte 1: current length
      chars    : ARRAY[1..maxLen] OF CHAR;    // Bytes 2..maxLen+1
   END_STRUCT;
END_TYPE

A fully populated STRING[254] occupies 256 bytes in the data block image. WinCC Flexible 2008 uses byte 0 (maxLen) to size the input/output field and byte 1 (actLen) to determine how many characters are valid in the current buffer. Without correct header bytes the HMI either shows nothing, shows garbage, or truncates the field at the wrong offset.

An ARRAY OF CHAR has no such header. The CP341 writes characters into the array starting at the configured start address and stops when the configured end condition is met (terminator character, character count, or frame end). The actual number of valid bytes is reported by the driver output, not embedded in the array.

Offset S7 STRING CP341 P_RCV buffer (declared as CHAR array)
Byte 0 maxLen Length high byte (typically 0)
Byte 1 actLen Length low byte (N, the number of valid data bytes)
Byte 2 chars[1] First character of the received data
Byte 3..N+1 chars[2..N] Subsequent characters
Byte N+2..end chars[N+1..maxLen] Stale or zero-padded

The CP341 receive buffer and the S7 STRING have the same on-the-wire layout, with the only difference being how the PLC declares the type at the DB. This is why the conversion is so cheap: in many cases the buffer is already a STRING, and the only thing missing is the type declaration or the HMI tag binding.

CP341 Driver and Receive Buffer Structure

The CP341 ships with three loadable protocol drivers, each of which uses different send/receive function blocks:

Protocol Send FB Receive FB DB Layout in Receive Buffer
ASCII FB2 P_SND FB3 P_RCV Byte 0-1: length, Byte 2..N+1: characters
3964R FB2 P_SND FB3 P_RCV Same as ASCII
RK512 FB6 P_SND_RK FB7 P_RCV_RK Byte 0-1: length, Byte 2..N+1: characters

The receive FB returns the number of valid bytes on its NDR (new data received) rising edge. The actual data is in the destination DB starting at byte 2. The standard pattern in OB1 is:

// Call FB7 P_RCV_RK
      CALL FB7, DB7
      LADDR   := 256                     // logical base address of CP341
      EN_R    := M 0.0                   // enable receive
      RCV_DB  := 43                      // destination = DB43
      LEN     := 254                     // max buffer length
      NDR     := M 10.0                  // new data flag
      ERROR   := MW 12                   // error code
      STATUS  := MW 14                   // status code

On the rising edge of M10.0, DB43.DBX 0.0 contains the high byte of the length (0), DB43.DBX 1.0 contains the low byte (N), and DB43.DBX 2.0 through DB43.DBX N+1 contain the barcode string.

Driver download: The loadable driver must be downloaded to the CP341 once after every module replacement or firmware update. Without the driver the CP341 SF LED lights and the P_RCV call returns STATUS = 0x0001. The download procedure is documented in the CP340/CP341 Point-to-Point Communication manual on the Siemens support portal.

Prerequisites

  1. The CP341 is parameterized in HW Config: correct slot, RS-232C or RS-422/485 interface, baud, parity, data bits, and stop bits matching the SICK CLV430.
  2. The loadable protocol driver has been downloaded to the CP341 from the parameterization disk. The SF LED on the module is off.
  3. FB6/FB7 (RK512) or FB2/FB3 (ASCII / 3964R) are present in the S7 program. Both FBs ship with STEP 7 V5.5 and live in the "CP PtP" library.
  4. DB43 is the receive buffer. The first two bytes of DB43 are written by the CP341 driver as the length field; the payload starts at DB43.DBX 2.0.
  5. The SICK CLV430 is configured to terminate each barcode with a recognizable character. The default is CR/LF; the ASCII driver recognizes any single terminator that the user assigns. Without a terminator the CP341 cannot detect end-of-message and the buffer never releases.
  6. The PLC and HMI are connected via MPI, PROFIBUS, or Ethernet, and the connection is configured in WinCC Flexible 2008 with the matching area pointer.

Method 1: Byte-by-Byte MOVE (Student's Approach)

The simplest conversion uses one MOVE (FBD) or L T (STL) per character to copy bytes from the CHAR array into the data area of a STRING tag, skipping the two header bytes. The original poster implemented this with explicit MOVE blocks in FBD after observing that the data area of both the receive buffer and the STRING starts at byte 2.

Layout in DB43 and a new DB44 that holds the conversion output:

DB Offset Symbol Type Description
DB43.DBX 0.0 rcv_len WORD Length field written by P_RCV_RK (byte 0 = high, byte 1 = low)
DB43.DBX 2.0 rcv_data ARRAY[0..253] OF CHAR 254-byte receive buffer
DB44.DBX 0.0 str_max BYTE STRING max length (set to 254)
DB44.DBX 1.0 str_act BYTE STRING actual length
DB44.DBX 2.0 str_data ARRAY[0..253] OF CHAR HMI-bound display string

FBD snippet (OB1, Network 1, evaluated after the P_RCV_RK NDR flag):

// Network 1: forward the payload into STRING
// trigger: NDR rising edge of FB7 P_RCV_RK
A   M 10.0                // NDR
JCN end

L   DB43.DBW 0            // length field from CP341
T   DB44.DBW 0            // becomes STRING header (max, act)

// BLKMOV is the cleanest single-call transfer
CALL SFC 20
SRCBLK  := P#DB43.DBX 2.0 BYTE 254
RET_VAL := MW 100
DSTBLK  := P#DB44.DBX 2.0 BYTE 254

end: NOP 0

This replaces the original "loop with MOVE blocks" approach with a single SFC20 call. The behavior is identical because the source and destination both use a 2-byte length header followed by the character payload. The student who started the thread with explicit per-byte moves arrived at the same final layout; the BLKMOV form is faster, easier to maintain, and bounded.

Disadvantages of the per-byte approach:

  1. Loop time scales with the number of characters; for a 30-character barcode it is fast, but for a 254-character payload the loop dominates the OB1 cycle.
  2. No automatic bounds check; if the scanner ever sends more than 254 bytes, the loop writes past the STRING boundary into the next DB variable.
  3. Hard to maintain: the structure depends on knowing the exact DB layout, and there is no documentation in the code itself.

Method 2: BLKMOV with Header STRUCT

SFC20 BLKMOV copies a contiguous block of bytes from one memory area to another. Used carefully, it can copy the entire CHAR array into the data area of a STRING in a single system call.

The trick is to declare the destination as a STRUCT whose first two members mirror the STRING header, and whose third member is a CHAR array of the same length as the source:

DATA_BLOCK DB44
   STRUCT
      header  : STRUCT
         maxLen : BYTE;                   // byte 0
         actLen : BYTE;                   // byte 1
      END_STRUCT;
      payload : ARRAY[0..253] OF CHAR;    // bytes 2..255
   END_STRUCT;
END_DATA_BLOCK

The address of payload[0] is DB44.DBX 2.0, which is the first character of the equivalent STRING[254]. Therefore the conversion procedure is:

  1. Pre-set DB44.DBX 0.0 (maxLen) to 254.
  2. Pre-set DB44.DBX 1.0 (actLen) to the P_RCV_RK length value (or to 0 if you want to clear stale data on a missed read).
  3. Call SFC20 BLKMOV with SRCBLK = P#DB43.DBX 2.0 BYTE n and DSTBLK = P#DB44.DBX 2.0 BYTE n, where n is the character count from the length field.

STL call pattern with explicit length handling:

      L     DB43.DBB 1                  // length low byte (N)
      T     DB44.DBB 1                  // STRING actLen

      L     254
      T     DB44.DBB 0                  // STRING maxLen

      L     DB43.DBB 1                  // N
      JPZ   end                         // skip BLKMOV if N = 0

      CALL  SFC 20
      SRCBLK  := P#DB43.DBX 2.0 BYTE 0   // patched at runtime
      RET_VAL := MW 100
      DSTBLK  := P#DB44.DBX 2.0 BYTE 0

Because SFC20 expects a static ANY pointer, the practical implementation in STEP 7 V5.5 is to use a literal length of 254 and trust the actLen byte to limit what the HMI displays. BLKMOV copies 254 bytes regardless; the trailing bytes are stale but masked by the actLen value.

Advantages:

  • Single SFC call, deterministic execution time of around 30 microseconds plus 0.6 microsecond per byte on a CPU 314C-2DP.
  • No loop, no arithmetic counter, no scratch byte.
  • Easier to maintain: the data block layout documents the conversion.

Caveats:

  • BLKMOV will not pad or truncate; if the scanner ever sends more than 254 bytes, the trailing characters spill past the destination. Either pre-check the length against 254 or grow the destination to match the worst case.
  • Both source and destination must be in work memory DBs or in the bit memory / process image. BLKMOV does not cross areas.
  • Stale data can persist in the destination between reads. Pre-fill the destination with B#16#00 using SFC21 FILL before the BLKMOV to clear it, or set the destination's actLen to 0 until a valid NDR is received.

Method 3: Reusable STL Function Block

For production code, encapsulate the conversion in a single FB so that the conversion logic is not duplicated in OB1. The FB takes a pointer to the source CHAR array, the source length, and a pointer to the destination STRING.

Block interface (FB45 "CharToString"):

I/O Symbol Type Description
IN src_ptr ANY Pointer to source CHAR array (P#DBxx.DBXy.0 BYTE n)
IN src_len INT Number of valid source bytes (0..254)
IN_OUT dest_str STRING[254] Destination STRING with header
OUT ret_val INT 0 = OK, 1 = src_len exceeds 254, 2 = src_len negative, 3 = pointer invalid
STATIC iCnt INT Internal loop counter
STATIC tmpByte BYTE Scratch byte

The implementation uses the LOOP instruction for the inner copy and pre-checks the bounds:

FUNCTION_BLOCK FB45
// Converts a flat CHAR array (any DB) to a STRING[254]
// Safe against overruns and negative lengths

      L     #src_len
      L     0
      I                                 // src_len > 254
      JCN   ck2
      L     1
      T     #ret_val
      BEA
ck2:  L     #src_len
      T     #dest_str.actLen             // STRING header byte 1
      L     254
      T     #dest_str.maxLen             // STRING header byte 0

      L     #src_len
      T     #iCnt
      L     0
      <=I
      JC    end                          // nothing to copy

next: L     #src_len
      L     #iCnt
      -I                                 // offset = src_len - iCnt
      SLD   3
      L     DW#16#84000000              // area = DB
      OD
      LAR1                              // AR1 -> source byte
      L     B [AR1,P#0.0]
      T     #tmpByte

      L     #src_len
      L     #iCnt
      -I
      L     2
      +I                                 // +2 for STRING header
      SLD   3
      L     DW#16#84000000
      OD
      LAR1                              // AR1 -> dest byte
      L     #tmpByte
      T     B [AR1,P#0.0]

      L     #iCnt
      LOOP  next

end:  L     0
      T     #ret_val
      BE

This implementation is conservative: it checks bounds, sets both header bytes, and processes characters in reverse so that any partial state during execution is not visible to a WinCC Flexible poll between two MOVE operations. In practice the FB is called from OB1 once per NDR rising edge from P_RCV_RK.

Note on IEC standard functions: STEP 7 V5.5 ships the IEC standard function FC5 STRING_TO and FC4 STRING_FROM in the "Standard Library > IEC Function Blocks" palette, but they work on full STRING types and do not help with raw CHAR arrays. The simpler approach is still the BLKMOV with header STRUCT or the per-NDR FB described above.

WinCC Flexible Tag Configuration

Once the destination STRING is in DB44, expose it to the HMI through an absolute tag. In WinCC Flexible 2008:

  1. Open the project, right-click "Tags" in the project tree, choose "New tag".
  2. Set the name to BarcodeDisplay and the data type to String.
  3. Set the connection to the PLC (MPI, PROFIBUS, or Ethernet as configured in the area pointer).
  4. Set the address to DB 44 DBW 2. WinCC Flexible 2008 addresses a STRING by the data start (the first character byte), not by the header. The display length is taken from the max length byte at DB44.DBX 0.
  5. Length: leave at 254. Acquisition mode: "Cyclic continuous". Acquisition cycle: 1 s for a barcode, or event-driven on a status bit for tighter response.

Place an I/O field or output field on the TP177A screen and bind it to BarcodeDisplay. In the property dialog set the display mode to "String" (the default for STRING tags is "Binary" if you change the data type after creation; re-verify in "Properties > Display").

Layout rules for the TP177A: the 6-inch TP177A has a 320 x 240 pixel screen. Use a single-line output field with a font height of at least 24 pixels so that a 30-character Code 128 barcode fits. For longer codes enable "Word-wrap" and increase the field height, or split the display into a 2-row layout (first 15 + last 15 characters).

Address alignment: WinCC Flexible 2008 reads the STRING starting at the first character byte. If you bind the tag to DB 44 DBB 0 instead, the HMI interprets the max/act length bytes as characters, and the field displays garbage or the maximum-length numeric value. Always verify the field start in the variable properties and confirm the offset matches the first character of the STRING.

Verification and Diagnostics

After commissioning, verify the conversion with the following procedure:

  1. Trigger a known barcode. Use a Code 128 label with a fixed test string such as TEST-12345.
  2. Open the S7 program online in STEP 7 and watch DB43.DBX 0..N. Confirm the ASCII bytes match the label and the length field reports 10.
  3. Watch DB44.DBX 0..1: byte 0 must be 254 (or the configured STRING max length), byte 1 must equal the length value from DB43.
  4. Watch DB44.DBX 2..N+1: the characters must match the label exactly, with no offset.
  5. Force FB7 NDR if the loop in Method 1 does not run, to confirm the trigger wiring is correct.
  6. From the TP177A online, open the screen with the output field and read the display. It must show TEST-12345.
  7. Trigger a barcode with embedded control characters (for example, a Code 128 with FNC1). Confirm the STRING is built correctly and the HMI does not lose characters.

Diagnostic aids:

  • The CP341 has an integrated diagnostic buffer accessible from STEP 7 via HW Config > Module Information. The buffer reports receive errors, framing errors, overrun, and protocol violations with time stamp.
  • The P_RCV_RK output ERROR / STATUS returns 0x0000 on success and 0x00xx on CP341 errors. Common values: 0x0001 (parameter error, DB not defined), 0x0010 (frame error, wrong baud or parity), 0x0080 (reception in progress).
  • Use the SICK SOPAS ET configuration tool to verify the scanner data format. Enable "echo on" temporarily to mirror the scanner input to a hyperterminal; this confirms whether the scanner is the source of malformed data.

Troubleshooting Matrix

Symptom Likely Cause Check Fix
HMI shows nothing STRING header wrong: maxLen = 0 or actLen = 0 Watch DB44.DBX 0..1 online; verify both bytes Set maxLen = 254 and actLen = EN_DAT before BLKMOV
HMI shows first character garbled Tag bound to wrong offset (DBB 0 instead of DBB 2) Open tag properties, check address Change address to first character byte of STRING
HMI shows stale data after a new barcode actLen not refreshed, or BLKMOV source zero Verify EN_DAT updates every scan Pre-set actLen = 0 or FILL destination with 0 before BLKMOV
First two characters are stripped from the display BLKMOV destination starts at byte 0 instead of byte 2 Inspect destination offset in SFC20 call Use P#DB44.DBX 2.0 BYTE n, not P#DB44.DBX 0.0
SF LED on CP341 lit Driver not loaded, or wiring wrong Check driver in module diagnostic buffer Re-download loadable driver; verify RS-232/422 pinout
Error 0x0010 from P_RCV_RK Baud rate or parity mismatch with SICK CLV430 Compare SOPAS settings vs CP341 HW config Match baud, data bits, parity, stop bits exactly
Data appears in DB43 but FB7 NDR never rises No terminator character defined on scanner SOPAS: read "Terminator" parameter Set terminator to CR, LF, or ETX as required by driver
CP341 reports receive overrun FB7 not called fast enough; receive buffer too long OB1 cycle time vs buffer size Move call to OB35 with priority, or shorten payload
Some barcodes display, others do not STRING length limit too short DBW 0 max length vs actual payload Increase maxLen to match longest expected barcode
String appears reversed in the HMI Loop processes from end to start (intentional in some FBs) Verify loop direction in code Reverse loop, or use BLKMOV with correct offsets
HMI shows "????" or hex Tag data type is BYTE or WORD instead of STRING WinCC Flexible: tag properties > data type Change data type to "String"

Frequently Asked Questions

Why can't WinCC Flexible 2008 display an ARRAY OF CHAR on a TP177A?

WinCC Flexible 2008 I/O fields and output fields render only the STRING data type for character data. A tag declared as ARRAY OF CHAR exposes individual bytes that the HMI runtime cannot interpret as a single text string. The fix is to declare a STRING tag at the address of the first character byte; the two-byte STRING header (max length, actual length) is read by the HMI just as it would be from any other STRING source.

Do I have to write the 2-byte STRING header manually in the PLC?

If the CP341 receive buffer is declared as a CHAR array, the first two bytes of the receive DB already contain a length field (high byte 0, low byte N) written by the driver. Copy those two bytes into the destination STRING's header bytes (maxLen = 254, actLen = N) before BLKMOV, and the HMI will read the field correctly. If you prefer, you can pre-declare the destination as STRING[254] in DB44 and skip the manual header handling because the S7 STRING type maps onto the same on-the-wire layout.

Is FB7 P_RCV_RK the correct choice for the SICK CLV430?

Usually not. The CLV430 default output is generic ASCII at 9600/8/N/1 with a CR/LF terminator, which matches the CP341 ASCII driver (FB2 P_SND, FB3 P_RCV). FB6/FB7 RK512 expects RK512 telegrams and adds protocol overhead. If the project uses FB7 and the scanner has not been re-configured for RK512, the CP341 will report framing errors. Either switch to FB2/FB3 and the ASCII driver, or reconfigure the scanner in SOPAS to send RK512 frames.

Can I use the IEC standard STRING functions from the STEP 7 V5.5 library?

FC4 STRING_FROM and FC5 STRING_TO work on full STRING types only. They do not help with raw CHAR arrays. For the CHAR-to-STRING conversion, use SFC20 BLKMOV with a 2-byte header STRUCT at the destination, or implement a small FB that bounds-checks the length and copies byte by byte.

Why does the HMI show stale data after a new barcode scan?

BLKMOV does not pad or clear the destination; only the number of bytes in the source is overwritten. If the new barcode is shorter than the previous one, the trailing characters remain in the STRING and are displayed by the HMI because actLen still reflects the new (shorter) value but the buffer was not zeroed. Pre-fill the destination with B#16#00 using SFC21 FILL before each BLKMOV, or set actLen to 0 until a valid NDR is received.

Will BLKMOV work if source and destination are in different DBs?

Yes. SFC20 BLKMOV accepts any work memory area as source and destination, including different data blocks. The P#DBxx.DBXy.0 BYTE n ANY pointer format is identical for source and destination. The only restriction is that the areas must be in the bit memory, process image, or work memory DB area; BLKMOV will not cross into the system memory or load memory.

Back to blog