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.
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.
STATUS = 0x0001. The download procedure is documented in the CP340/CP341 Point-to-Point Communication manual on the Siemens support portal.
Prerequisites
- 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.
- The loadable protocol driver has been downloaded to the CP341 from the parameterization disk. The SF LED on the module is off.
- 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.
- 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.
- 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.
- 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:
- 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.
- 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.
- 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:
- Pre-set DB44.DBX 0.0 (maxLen) to 254.
- 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).
- Call SFC20 BLKMOV with
SRCBLK = P#DB43.DBX 2.0 BYTE nandDSTBLK = 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
FILLbefore 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:
- Open the project, right-click "Tags" in the project tree, choose "New tag".
- Set the name to
BarcodeDisplayand the data type toString. - Set the connection to the PLC (MPI, PROFIBUS, or Ethernet as configured in the area pointer).
- 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. - 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).
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:
- Trigger a known barcode. Use a Code 128 label with a fixed test string such as
TEST-12345. - 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.
- 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.
- Watch DB44.DBX 2..N+1: the characters must match the label exactly, with no offset.
- Force FB7 NDR if the loop in Method 1 does not run, to confirm the trigger wiring is correct.
- From the TP177A online, open the screen with the output field and read the display. It must show
TEST-12345. - 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.