Siemens S7 R_STRG to OPC: Converting Real Numbers to Readable Strings
The Siemens STEP 7 function block FC30 (R_STRG) converts REAL values into STRING format, but the output is always rendered in fixed scientific notation. When this string is exposed through an OPC server to a third-party traceability system or SCADA, the result is an awkward display value such as +0.18 instead of the expected 1.83. The defect is not in the FC30 function and not in the OPC communication link—both are operating per specification. The issue is the selection of representation: FC30 was designed for legacy operator panels that pre-date floating-point tag support, not for OPC clients that expect decimal notation.
This article examines the underlying string format, the OPC data-type boundary, and four field-proven solutions. Complete STEP 7 ladder, FBD, and SCL code samples are provided, along with verification procedures, OPC UA data-type mapping notes, and a diagnostic matrix for tracing similar string-handling defects in production cells.
1. Problem Description
A STEP 7 (S7-300 / S7-400) user program calls FC30 R_STRG to convert a REAL measurement (for example, 1.8324) into a STRING that is then transmitted to an external traceability system via OPC. The conversion produces a 24-byte internal string with the layout +0.1832400E+01. When the OPC server exposes this block to a third-party OPC client, the client typically reads the first 5 to 8 ASCII characters for display, yielding +0.18—a value that is numerically meaningless to a human operator or a downstream system.
1.1 Symptom Summary
| Element | Observed Value |
|---|---|
| Source REAL (DB15.DBD48) | 1.8324 |
| FC30 internal string buffer (DB5, String[24]) | '+0.1832400E+01' (14 visible characters + length bytes) |
| OPC tag exposed as DB2.string5 (5-char view) | '+0.18' |
| Expected OPC tag value | '1.83' or '0.00' (decimal fixed-point) |
| Downstream consumer | Traceability MES expecting ASCII decimal |
1.2 Environment
- PLC: SIMATIC S7-300 (CPU 315-2 DP/PN or equivalent) or S7-400 (CPU 414/416).
- OPC server: SIMATIC NET OPC server (S7-200 PC Access, S7-1200/S7-1500, or legacy S7-300/400), Kepware KEPServerEX, or a third-party gateway such as Ignition's Siemens driver.
- OPC client: a third-party traceability system that consumes ASCII STRING data (e.g., MES, line-tracking software).
- Transport: Industrial Ethernet (PROFINET) on TCP/IP port 102 (S7 communication) or OPC UA on port 4840.
FC30 is replaced by built-in conversions and the new DINT_TO_STRING / REAL_TO_STRING with format specifiers). String handling and OPC UA String data type are discussed in section 8.2. Root Cause Analysis
FC30 (R_STRG) is part of the standard STEP 7 conversion library (Standard Library → TI-S7 Converting Blocks). The block accepts a 32-bit REAL input and writes an ASCII representation into a STRING buffer. The function does not accept any format specifier; the output layout is hard-coded as scientific notation with the following template:
[sign]0.dddddddE[sign]ee
This template is fixed for the following reasons:
- Legacy compatibility with 4-line text displays (TD17, OP7, OP17). Early Siemens operator panels could not render floating-point values directly. The firmware in those panels called the same conversion function. To keep a single source of truth, the format was fixed.
- Deterministic string length. A fixed scientific format guarantees the output always fits in the 14-character body of a String[16] or larger buffer. Format specifiers would risk overflow.
- Lossless round-trip. Scientific notation preserves the full IEEE 754 precision. Truncating the mantissa or rounding to a fixed decimal would lose accuracy for traceability records.
When the STRING is exposed to OPC, the data-type boundary is crossed. The S7 STRING data type has a 2-byte header (maximum length, current length) followed by ASCII bytes. The OPC UA String data type, as defined in the OPC UA specification (Part 3, section 8.21), is a Unicode UTF-8 byte sequence with no header. The OPC server must perform the translation. Most servers strip the S7 header and expose only the ASCII body. The OPC client then sees an opaque byte sequence and simply renders the leading characters. With a 5-character view of a 14-character scientific-notation string, the rendered value is meaningless.
Defect chain summary: The choice of FC30 enforces scientific notation → the OPC client cannot interpret a 14-byte scientific string as decimal → the truncated display is mathematically incorrect → the traceability system receives unparseable data.
3. Solution Decision Matrix
Four engineering approaches resolve the defect. Each has different cost, code footprint, and migration risk. The matrix below is the basis for selecting an approach for a given installation.
| Solution | Code in PLC | Code in OPC client | Latency | Migration risk | Best for |
|---|---|---|---|---|---|
| 1. Expose REAL; format in OPC client | None | Format string | Lowest | Low (only SCADA change) | SCADA-only consumption, no MES |
| 2. Use STEP 7 conversion library variants | Replace FC30 with FC39, FC16, FC17 | None | Low | Low (block swap) | Integer-only data with leading zero suppression |
| 3. Build decimal string manually in S7 | FC30 + custom string-build | None | Medium | Medium (custom code) | Strict decimal format, fixed width, traceability |
| 4. Use S5 conversion blocks (FC93-100) | FC93-FC100 string conversions | None | Medium | High (legacy blocks) | S5 migrations, asymmetric formats |
4. Solution 1: Expose REAL Directly and Format in the OPC Client
The cleanest fix is to remove the FC30 conversion entirely and expose the 32-bit REAL value as a native OPC tag. The OPC client (SCADA, HMI, or MES) is responsible for formatting the number into a human-readable string using its built-in display function.
4.1 STEP 7 Configuration
Remove the FC30 call. Expose DB15.DBD48 as a 4-byte REAL OPC tag. The DB must be non-optimized (classic DB) so the OPC server can map the tag by absolute address. In a STEP 7 DB definition, mark the relevant area as:
REAL "MeasuredValue"; // DB15.DBD48
In the SIMATIC NET OPC server, the tag is configured as S7:[S7 connection_1]DB15,REAL48 (byte-offset form) or as the symbolic name MeasuredValue. The OPC client receives a VT_R4 / IEEE 754 32-bit float. Formatting is then a client-side operation.
4.2 OPC Client Format Examples
Most OPC clients have a format-string function. Common patterns:
-
WinCC / TIA Portal HMI: Output field property → Format pattern:
999.99or0.00. The runtime converts REAL to text per the format specifier. -
Ignition: Bind the OPC tag to a Label component; set Format String to
{:.2f}(Python format specifier). Two decimal places are enforced. - C# OPC client (OPC Foundation .NET Standard):strong> use
value.ToString("0.00", CultureInfo.InvariantCulture)to guarantee locale-independent decimal output. -
Python (OpenOPCUA / asyncua):
f'{value:.2f}'returns a string with two decimal places.
Trade-off: This is the cleanest engineering fix but requires the downstream MES or traceability system to support REAL data. If the traceability vendor contract mandates a STRING, this approach is not viable.
5. Solution 2: Use the STEP 7 Standard Conversion Library
If the OPC tag must remain a STRING, but the source data is a DINT or INT (not a floating-point REAL), use the integer-to-string functions in the same TI-S7 library. The function set is summarized below.
| Block | Name | Input type | Output format | Notes |
|---|---|---|---|---|
| FC16 | I_STRNG | INT (16-bit) | '-32768' to '32767' with leading sign | 6-character max output |
| FC17 | DI_STRNG | DINT (32-bit) | '-2147483648' to '2147483647' | 11-character max output |
| FC30 | R_STRNG | REAL (32-bit float) | '+0.dddddddE+ee' (always scientific) | 14-character output, fixed format |
| FC39 | STRING_DI | STRING → DINT | Reverse of FC17 | String-to-DINT parser |
| FC40 | STRING_R | STRING → REAL | Reverse of FC30 | String-to-REAL parser |
For the case where the source is a scaled integer (e.g., 18324 represents 1.8324 with implicit 1e-4 scaling), FC17 DI_STRNG produces a clean decimal string '18324'. The OPC client then adds the decimal point per the scale factor. This is a common practice in weight and length measurements where fixed-point scaling is used.
5.1 Ladder Logic Example (FC17 DI_STRNG)
The call interface is identical to FC30:
NETWORK 1: Convert scaled DINT to STRING
CALL "DI_STRNG"
IN :=DB15.DBD48 // DINT 18324
RET_VAL:=DB5.STRING2 // '18324'
Then the OPC client (or a small SCADA script) inserts the decimal point. Example Python:
raw = await opc_tag.read() # '18324'
val = float(raw) / 10000.0 # 1.8324
formatted = f'{val:.2f}' # '1.83'
6. Solution 3: Build the Decimal String Manually in STEP 7
When the MES contract demands a fixed-width ASCII decimal string, the only fully deterministic solution is to build the string character-by-character in the S7 user program. The algorithm decomposes the REAL into integer and fractional parts, then writes ASCII codes 0x30..0x39 (the digits '0'..'9') plus a decimal separator (typically 0x2E for '.' or 0x2C for ',' per locale) into a DB string buffer.
6.1 Algorithm Overview
- Multiply the REAL by 10^N (where N is the number of decimal places desired). For 1.8324 with N=2, this becomes 183.24.
- Apply
TRUNC(FC at address 0x00, built-in in STEP 7) to obtain the integer 183. - Extract hundreds, tens, and units digits using DIV 100, MOD 10, etc.
- Add 48 (0x30) to each digit to produce the ASCII code.
- Insert a decimal separator character at the correct position.
- Write the resulting characters into the STRING buffer, set the length byte, and zero the remainder.
6.2 SCL (Structured Control Language) Implementation
SCL is the most concise option for this algorithm. The function below builds a 6-character decimal string in the form X.YY (one integer digit, decimal point, two fractional digits). Adjust the constants for different widths.
FUNCTION FC100 : VOID
VAR_INPUT
rValue : REAL; // 1.8324
iDecimals : INT := 2; // precision
END_VAR
VAR_IN_OUT
sOut : STRING[8]; // target buffer
END_VAR
VAR_TEMP
rScaled : REAL;
diScaled : DINT;
iIntPart : DINT;
iSign : INT;
arrBytes : ARRAY[1..8] OF BYTE;
i : INT;
END_VAR
BEGIN
// Handle sign
IF rValue < 0.0 THEN
iSign := 1;
rScaled := -rValue;
ELSE
iSign := 0;
rScaled := rValue;
END_IF;
// Scale to integer
rScaled := rScaled * 10.0 ** iDecimals;
diScaled := DINT_TO_DINT(TRUNC(rScaled + 0.5));
// Build digits into a temporary byte array
FOR i := 1 TO 8 DO arrBytes[i] := 0; END_FOR;
arrBytes[2] := B#16#2E; // '.' separator at position 2
diScaled := DINT_TO_DINT(DI_MOD(IN:=diScaled, OUT:=diScaled)); // safety
// Integer part (one digit) and fractional part
iIntPart := diScaled / 100;
arrBytes[1] := INT_TO_BYTE(iIntPart + 48);
arrBytes[3] := INT_TO_BYTE(DI_MOD(IN:=diScaled/10, OUT:=iIntPart) + 48);
arrBytes[4] := INT_TO_BYTE(DI_MOD(IN:=diScaled, OUT:=iIntPart) + 48);
// Sign prefix if needed
IF iSign = 1 THEN
arrBytes[1] := B#16#2D; // '-'
END_IF;
// Copy to STRING buffer (header + body)
sOut[0] := 0; // S7 STRING header: max length (set by editor)
sOut[1] := 4; // actual length = 4 (e.g., '1.83')
FOR i := 1 TO 4 DO
sOut[i+1] := CHAR_TO_BYTE(CHAR(arrBytes[i]));
END_FOR;
END_FUNCTION
6.3 Why the Custom Function Is Necessary
FC30 has no format specifier, no decimal-place parameter, and no way to suppress the scientific notation. The standard library does not include a "REAL to decimal string with N places" block. The only Siemens function that provides this is the legacy S5 conversion block set (FC93 through FC100, S5-S7 Converter), which has a different call interface and is not always installed. Writing the conversion in SCL or in STL is the practical solution.
7. Solution 4: Use S5 Legacy Conversion Blocks
For S5-to-S7 migration projects, the S5 conversion library provides fixed-point output blocks. The relevant blocks are:
| Block | Function | Input | Output format |
|---|---|---|---|
| FC93 | KI to string | INT (16-bit) | Right-aligned, leading spaces |
| FC94 | KD to string | DINT (32-bit) | Right-aligned, leading spaces |
| FC95 | KF to string | REAL (32-bit) | Right-aligned, leading spaces, no exponent |
| FC96 | KG to string | REAL (floating-point) | Right-aligned, with decimal point, no exponent |
FC96 KG_STRING is the closest analog to a "decimal REAL to STRING" function. Its output is a fixed-width, right-aligned decimal representation without scientific notation. The block is installed in the Standard Library under "S5-S7 Converting Blocks". Migrating existing S5 code that already calls FC95/FC96 is straightforward: include the library in the STEP 7 project, copy the block, and call it with the same parameter pattern.
Trade-off: The blocks are deprecated and not supported on S7-1200/1500. They should only be used to preserve existing logic, not for new development.
8. OPC UA String Data Type Mapping
When the OPC server is OPC UA (not classic OPC DA), the S7 STRING must be mapped to the OPC UA String data type. The OPC UA specification defines String in Part 3, section 8.21, as a sequence of Unicode characters encoded in UTF-8, with no header bytes.
The OPC UA standard does not include a "fixed-point REAL" data type. Floating-point values are exposed as Float (IEEE 754 32-bit) or Double (64-bit). Clients that need a decimal string must format the value themselves.
| OPC UA built-in type | Identifier (numeric) | Identifier (string) | S7 equivalent |
|---|---|---|---|
| Boolean | 1 | Boolean | BOOL |
| SByte | 2 | SByte | INT (low byte) |
| Int16 | 4 | Int16 | INT |
| Int32 | 6 | Int32 | DINT |
| Float | 10 | Float | REAL |
| String | 12 | String | STRING (header stripped) |
| DateTime | 13 | DateTime | DATE_AND_TIME |
9. Verification Procedure
After implementing one of the solutions above, perform the following verification steps in the order given.
9.1 STEP 7 Online Watch Table
- Open the project in STEP 7, connect online to the PLC (TCP/IP via NetPro).
- Open a VAT (Variable Table) and monitor the source REAL, the FC30 (or alternative) output, and the STRING buffer's length byte.
- Force the source REAL to known test values: 0.0, 1.8324, -0.001, 12345.6789, -12345.6789.
- Confirm the STRING length byte reflects the correct ASCII count.
9.2 OPC Client Browsing
- Open the OPC client (e.g., OPC Scout V10 for SIMATIC NET, UA Expert for OPC UA, or the KEPServerEX Quick Client).
- Browse to the tag. Confirm the data type matches the expected type (String, Float, etc.).
- Read the current value. Confirm the value matches the expected display.
- For STRING tags, use a hex viewer (e.g., UA Expert → DataType View → Show as Hex) to verify the byte sequence.
9.3 End-to-End Trace
- Trigger a known test value from the PLC (e.g., write 1.8324 to DB15.DBD48).
- Capture the value at the OPC server (log to a CSV file for 5 seconds).
- Capture the value at the OPC client.
- Compare all three values (PLC source, OPC server, OPC client). They must be numerically equal.
9.4 Round-Trip Test
For traceability systems, perform a round-trip test: write a STRING from the OPC client back to the PLC, parse it with FC40 STRING_R, and confirm the resulting REAL matches the original. This validates the complete data chain including the parser's tolerance of the decimal separator (',' vs '.').
10. Troubleshooting Matrix
| Observed symptom | Likely cause | Verification | Resolution |
|---|---|---|---|
| OPC tag shows '+0.18' for 1.8324 | FC30 scientific output truncated by client display width | Check STRING length byte (should be 14, not 5) | Switch to Solution 1, 2, or 3 |
| OPC tag shows garbage characters at the start | S7 STRING header (length bytes) not stripped by server | Hex view of OPC tag value | Configure server to strip header, or use offset 2 in client |
| OPC tag shows empty string | Length byte in S7 STRING is 0 | VAT monitoring | Re-trigger FC30; check the RET_VAL parameter is the STRING buffer pointer, not the STRING variable itself |
| OPC client cannot browse STRING tags | Server does not support S7 STRING data type | Server documentation; tag data type in OPC browser | Use Solution 1 (REAL exposure) and format on client |
| String contains '.' but downstream expects ',' | Locale-specific decimal separator in FC30 | Hex view; check for 0x2E vs 0x2C | Format string in client with locale-aware formatter |
| String length exceeds OPC server limit | Server truncates at 80 or 255 bytes | Server documentation | Reduce string width; use multiple tags for long values |
| Downstream system cannot parse the string | Trailing nulls or padding characters | Hex view of last 2 bytes | Set length byte to actual character count; do not include null terminator |
11. Best Practices for S7 STRING and OPC Integration
- Avoid converting REAL to STRING in the PLC unless the downstream contract mandates it. Expose the REAL natively and let the SCADA/MES format the value. This eliminates FC30 format limitations and locale issues.
- Reserve the STRING data type for legacy MES contracts only. If a string is required, build it in a structured way (Solution 3) rather than relying on FC30's scientific output.
- Always set the S7 STRING length byte (byte 1) explicitly. A wrong length byte causes the OPC server to expose incorrect characters, even if the body bytes are correct.
- Use FC30 only when the downstream panel or system specifically requires scientific notation. For all other cases, use integer conversion (FC16/FC17) with implicit scaling, or a custom decimal function.
- Confirm the OPC server strips the S7 STRING header. SIMATIC NET, Kepware, and Ignition do; some older servers do not. Always verify with a hex view.
- Use locale-invariant decimal separators. Build strings with '.' (0x2E) in the PLC; let the client handle locale conversion if needed.
-
For TIA Portal projects on S7-1200/1500, use the built-in
REAL_TO_STRINGwith format specifiers. The TIA environment does not have FC30; the modern function allows controlled precision without scientific notation.
12. Migration Path: STEP 7 to TIA Portal
For sites migrating from S7-300/400 to S7-1200/1500, the FC30 issue disappears entirely. The TIA Portal runtime supports the REAL_TO_STRING function in SCL with a format-string parameter:
// TIA Portal SCL
sOut := REAL_TO_STRING(rValue); // default format
sOut := LREAL_TO_STRING(rValue, 0, 2); // explicit: width 0, 2 decimals
The modern function produces a decimal string directly, without scientific notation. When migrating, replace the FC30 call with the TIA equivalent and remove any OPC client workarounds that compensated for the FC30 format.
String data type with no header. The integration is much cleaner than the SIMATIC NET-based S7-300/400 path. Sites planning an OPC UA upgrade should consider the S7-1500 transition as part of the architecture refresh.13. References and Standards
- Siemens STEP 7 V5.5 Standard and System Function Reference Manual (entry ID 1214574 on the Siemens Industry Online Support portal) — defines FC30, FC16, FC17, FC39, FC40 and the STRING data type layout.
- Siemens SIMATIC NET OPC Server Manual (S7-300/400) — documents the S7 STRING to OPC UA String mapping, including the header-stripping behavior.
- OPC Unified Architecture, Part 3: Address Space Model, section 8.21 — String data type definition, identifiers, and case-sensitivity rules. Available at the OPC Foundation specification repository.
- Siemens Industry Online Support portal — the primary source for STEP 7, TIA Portal, and SIMATIC NET manuals, firmware notes, and application examples.
Why does FC30 always output scientific notation?
FC30 is a legacy conversion function designed for operator panels that pre-date floating-point display support. The output format is hard-coded as ‘+0.dddddddE+ee’ with a fixed 14-character body. The function does not accept a format parameter; there is no way to change the output within FC30 itself. To get decimal output, use a different conversion block, build the string manually in SCL, or format the value in the OPC client.
Can I configure FC30 to output decimal format?
No. FC30 has no input parameters for format, decimal places, or width. The output is always scientific notation. The only way to produce decimal output in the PLC is to use a different block (e.g., FC96 from the S5-S7 library) or to write a custom conversion function in SCL or STL using TRUNC, MOD, and direct byte writes to the STRING buffer.
Why does the OPC tag show only 5 characters?
OPC tag definitions typically specify a fixed width (e.g., 5 characters for a 5-character display). When the underlying STRING is 14 characters long, the OPC server exposes only the first 5 bytes to fit the tag definition. The fix is either to expand the tag width to 14 (and accept the scientific notation) or to change the data type / value to a decimal string that fits the 5-character limit. Most installations choose the latter.
Does the OPC server strip the S7 STRING header?
Modern servers (SIMATIC NET, Kepware KEPServerEX, Ignition) strip the 2-byte S7 STRING header automatically and expose only the ASCII body. Older servers, especially legacy OPC DA servers, may not. The S7 STRING layout is: byte 0 = max length, byte 1 = current length, bytes 2..N+1 = ASCII characters. If the header is not stripped, the OPC client receives a 2-character offset. Always verify with a hex view of the OPC tag value.
Is there a TIA Portal equivalent of FC30?
Yes, the TIA Portal S7-1200/1500 environment uses the built-in conversion function REAL_TO_STRING (in SCL) with explicit format parameters. The TIA function supports decimal notation directly, with a configurable number of decimal places. The TIA equivalent is therefore not a 1:1 drop-in for FC30 — the migration usually produces cleaner output and removes the need for OPC-side workarounds.