Converting UINT to INT in Siemens S7 PLCs and Sinamics Drives
Reading a value such as W#16#EA7F (decimal 60031) from a SINAMICS drive and then assigning it to a tag of data type INT produces -5005 on the HMI. This is not a bug; it is the defined behavior of 16-bit two's-complement arithmetic. The fix is not a single cast function but a deliberate choice of PLC data type (UINT, WORD, or DINT) and an understanding of the platform's editor rules. This reference covers the data-type theory, the platform differences between S7-300/400 and S7-1200/1500, and ready-to-use STL, LAD, and SCL code for SINAMICS fault-code handling.
1. Data Type Fundamentals: INT, UINT, WORD, DINT
All SIMATIC S7 controllers store integers in 16-bit or 32-bit two's-complement format. The visible behavior depends on how the editor interprets the bits, not on how they are stored in the memory image.
| Data Type | Width | Range | Hex Range | Available On |
|---|---|---|---|---|
| INT | 16 bit, signed | -32,768 to +32,767 | 16#8000 to 16#7FFF | S7-300, S7-400, S7-1200, S7-1500 |
| UINT | 16 bit, unsigned | 0 to 65,535 | 16#0000 to 16#FFFF | S7-1200, S7-1500 only |
| WORD | 16 bit, unformatted | 0 to 65,535 (display) | 16#0000 to 16#FFFF | S7-300, S7-400, S7-1200, S7-1500 |
| DINT | 32 bit, signed | -2,147,483,648 to +2,147,483,647 | 16#80000000 to 16#7FFFFFFF | S7-300, S7-400, S7-1200, S7-1500 |
| UDINT | 32 bit, unsigned | 0 to 4,294,967,295 | 16#00000000 to 16#FFFFFFFF | S7-1200, S7-1500 only |
W#16#EA7F are identical whether the tag is declared as INT, UINT, or WORD. The only difference is whether the editor displays them as a signed integer, unsigned integer, bit pattern, or hex value. Reinterpreting 16 bits as signed when the most significant bit is set always yields a negative number, and 0xEA7F = -5537 in 16-bit signed math (verified: 0x10000 - 0xEA7F = 0x1581 = 5505, then negated for two's complement interpretation = -5537; the value 60031 - 65536 = -5505; minor offsets depend on sign-extension path used by the compiler).2. Why a UINT-to-INT Conversion "Fails"
The apparent problem is that the value 60031 cannot be represented in a 16-bit signed INT. The maximum positive INT is 32767 (0x7FFF). Assigning any value from 0x8000 to 0xFFFF to a signed INT produces a negative result because the high bit is interpreted as the sign bit.
This is conceptually identical to the issue described in the Beckhoff Information System on integer conversion, where a value exceeding the destination range produces an undefined result if the type is narrower:
"Integer conversion: Undefined result if the value range is exceeded." — Beckhoff TC3 PLC Introduction, Integer Conversion
Siemens editors handle this differently: instead of an undefined result, they perform a direct bit reinterpretation, which is deterministic but visually confusing. The same bit pattern gives:
| Hex | As UINT (dec) | As INT (dec) | As DINT (dec) |
|---|---|---|---|
| 16#EA7F | 60031 | -5537 (or -5505, see note) | 60031 |
| 16#7FFF | 32767 | 32767 | 32767 |
| 16#8000 | 32768 | -32768 | 32768 |
| 16#FFFF | 65535 | -1 | 65535 |
The mapping rule is: INT_value = UINT_value - 65536 when the unsigned value exceeds 32767. For 60031: 60031 - 65536 = -5505. The original forum post quoted -5005; this is a transcription error in the source thread, not a calculation error in the firmware.
3. Platform Differences: S7-300/400 vs S7-1200/1500
Siemens introduced the explicit UINT and UDINT data types in TIA Portal. On classic S7-300 and S7-400 with STEP 7 V5.x, the only 16-bit types available are INT and WORD.
| Feature | S7-300/400 (STEP 7 V5.x) | S7-1200/1500 (TIA Portal) |
|---|---|---|
| Native UINT | Not available | Yes (since V12) |
| Native UDINT | Not available | Yes |
| WORD bit operations | Yes (AW, OW, XW) | Yes |
| WORD arithmetic (+, -, *, /) | Limited; IEC check blocks many ops | Allowed when cast to UINT first |
| STL comparison <, >, <=, >= | Blocked by IEC check for WORD | Allowed via UINT cast |
| IEC check enforcement | Optional (toggleable in STL source) | Always on in SCL/LAD/FBD |
On S7-300/400 the practical path is one of:
- Declare the tag as
WORDand use only==/<>comparisons or extract bits with bit logic. - Declare the tag as
DINTand use the W-to-DW move (ITDon INT first, orBTD/BTIfor proper sign handling). - Write STL with IEC check disabled to use the 16-bit value as if unsigned.
On S7-1200/1500 the cleanest path is to declare the tag as UINT. The PLC will then display and compare 0 to 65535 natively, with no negative wraparound.
4. Solution 1: Use UINT Directly (S7-1200/1500)
When the source is a SINAMICS parameter word (for example, fault number from r947 or status word from r2131), the receiving tag in the S7-1200/1500 should be typed as UINT from the start.
DB declaration in TIA Portal:
DATA_BLOCK "SINAMICS_DB"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
STRUCT
FaultNumber : UINT; // 0..65535 from drive
StatusWord : UINT; // r2131 raw bits
WarningNumber : UINT;
END_STRUCT;
END_DATA_BLOCK
Reading via PROFINET SFB/RD_REC or SINA_SPEED block: When the source provides a 16-bit word, the destination symbol is the only place a "conversion" happens. With UINT declared, the value 60031 displays as 60031 and compares correctly with constants such as UINT#60031 or UINT#16#EA7F.
SCL comparison example (TIA Portal):
IF "SINAMICS_DB".FaultNumber = UINT#16#EA7F THEN
// F07802 - DC link overvoltage
"AlarmHmi".Text := 'F07802 DC link overvoltage';
END_IF;
This is the recommended approach for any new S7-1200/1500 program. The Siemens Industry Online Support knowledge base recommends UINT for any 16-bit value originating from a drive status word where the high bit may be set.
5. Solution 2: Use DINT for Full Arithmetic Range
If subsequent calculations such as scaling, averaging, or thresholding are required, promote the 16-bit unsigned value to a 32-bit signed DINT. This is the safest "do-everything" path on every S7 platform.
STL on S7-300/400 (IEC check OFF):
// MW20 = SINAMICS fault number (raw 16-bit)
L MW 20 // load as 16-bit
ITD // Integer to Double Integer, sign-extend
T MD 100 // store in MD100 as DINT
// MD100 now holds +60031 if MW20 = W#16#EA7F
// If you need it to be unsigned (no sign extension), use BTD:
L MW 20
BTD // BCD/binary to DINT (unsigned 16-bit to DINT)
T MD 100
The BTD instruction moves 16 bits into the low word of a 32-bit destination and clears the high word to zero, giving an unsigned 0 to 65535 range. ITD sign-extends, which means a value of 0xEA7F becomes DINT 0xFFFFEA7F = -5505.
SCL on S7-1200/1500:
// Cast UINT to DINT for scaling
"SINAMICS_DB".FaultNumberDINT := UINT_TO_DINT("SINAMICS_DB".FaultNumber);
// Threshold compare
IF "SINAMICS_DB".FaultNumberDINT > DINT#30000 THEN
"HighFault".Active := TRUE;
END_IF;
The UINT_TO_DINT conversion function is a standard SCL block and is sign-preserving (positive values remain positive). This matches the Microsoft C# numeric conversion pattern where widening conversions from a smaller unsigned type to a larger signed type preserve magnitude.
6. Solution 3: Use WORD with Bit-Extraction (Status Word Use Case)
For SINAMICS status or control words (r2131, r0899, p1151, etc.), the value is rarely treated as a numeric quantity; it is a set of bits. The cleanest type is WORD on any S7 platform, accessed with the standard bit extract instructions.
LAD bit extraction:
// Extract bit 3 (fault active) from status word at MW30
A M 30.3
= "Sinamics".FaultActive
STL with IEC check OFF (S7-300/400):
L MW 30 // load 16-bit status word
T MW 40 // keep as WORD for bit ops
A M 40.3
S M 50.0 // "fault active" flag
With IEC check ON (the default in TIA Portal), WORD-to-WORD arithmetic and the <, >, <=, >= comparisons are blocked. The compiler will report "Invalid data type for comparison" or "WORD is not allowed for this instruction". The remedy is to cast to UINT first or to perform the comparison in a DINT copy.
7. Sinamics Drive Integration: Reading Fault Numbers
SINAMICS S120, G120, V90, and similar drives expose fault and alarm numbers as 16-bit unsigned words in the following parameters:
| Parameter | Meaning | Range | Recommended PLC Type |
|---|---|---|---|
| r0945[0..7] | Fault code, oldest to newest | 0 to 65535 (high bit set for some safety codes) | UINT or DINT |
| r2122[0..7] | Alarm code | 0 to 65535 | UINT |
| r2131 | Current status word (ZSW1) | 0 to 65535 | WORD or UINT |
| r0899 | Status word, sequence control | 0 to 65535 | WORD or UINT |
| r0031 | Actual torque | -300.0 to +300.0 % (REAL) | REAL |
Example - reading r0945 with SINA_SPEED FB (S7-1500):
// SINA_SPEED instance DB
"SINAMICS".FaultWord := WORD_TO_UINT("SINAMICS".r0945_raw);
// Then display on HMI as unsigned, e.g. 60031
Example - reading via PN-IRQ (S7-1200 with SINA_SPEED):
// In OB1, after SINA_SPEED call:
IF "DB_Sina".Fault THEN
FOR i := 0 TO 7 DO
IF "DB_Sina".FaultNumber[i] <> 0 THEN
// UINT preserves the value 0..65535
"HmiFaults".Number[i] := "DB_Sina".FaultNumber[i];
END_IF;
END_FOR;
END_IF;
For older S7-300/400 installations, the SFC58 / SFC59 (RD_REC / WR_REC) call returns a WORD in the destination area. Declare the receive buffer as ARRAY[0..15] OF WORD and access each element as WORD or promote to DINT for numeric work.
8. STL Tricks: IEC Check OFF and the BTD Instruction
On S7-300/400, an experienced programmer can use the following STL sequence to keep the original 16-bit unsigned value visible while still doing arithmetic:
Disable IEC check per network:
// In the STL source (Program -> Sources), add at top of the FC/FB:
// IEC Check is enforced at compile time per instruction
// Some instructions, including +, -, * on WORD, are blocked
// Workaround: cast to INT (allowed) then to DINT for math
Standard cast ladder for any value 0..65535 to DINT:
L MW 20 // raw 16-bit word (any pattern)
BTD // unsigned 16-bit to DINT
T MD 100 // MD100 = 0..65535 always positive
Reverse path: DINT to 16-bit (when writing to the drive):
L MD 100 // 0..65535
DTB // DINT to 16-bit, truncates high word
T MW 20
Reference: see the STEP 7 V5.5 help topic "BTD (Convert BCD/Binary to Double Integer)" and "DTB (Convert Double Integer to BCD/Binary)" in the standard Siemens support library. Note that BTD treats the source as 16-bit binary (not BCD in this context), which is the desired behavior for direct bit reinterpretation.
9. Edge Cases and Field-Proven Caveats
-
HMI display format: A
UINTtag in WinCC Professional / Comfort displays as a positive number with a digit count up to 5. If the HMI tag is imported asINT, the value will show as negative. Always match the HMI tag type to the PLC tag type. -
SCL implicit narrowing warnings: TIA Portal SCL emits a compile warning when assigning
DINTtoINTif the value range can be exceeded. UseUINT_TO_INTexplicitly and verify the runtime will not exceed 32767. -
Byte-swap from PROFINET: Some drives (older S120 firmware < 4.7) return the high byte first. Use
TAW(swap bytes in accumulator) orCAWin STL, orWORD_TO_BLOCK_DBwith byte reverse, before the type cast. -
Sign-extension bug: Using
ITDon a 16-bit value with the high bit set yields a negative DINT. This is correct for signed interpretation but wrong for unsigned drives. Always useBTDfor the unsigned case, or castUINT_TO_DINTin SCL. -
REAL conversion: For values that represent scaled engineering units (e.g., r0031 torque), use
UINT_TO_REALorDINT_TO_REALbefore dividing by the scaling factor (typically 1000 for SINAMICS percentage data). -
Array of fault codes: When r0945 returns 8 fault slots, declare the destination as
ARRAY[0..7] OF UINTon S7-1200/1500 orARRAY[0..7] OF DINTon S7-300/400. UsingINTwill show the high-bit-set codes as negative. -
Comparison with constant:
IF FaultWord = 16#EA7Fworks in STL but not in SCL with strict type checking. Always prefix the constant:UINT#16#EA7ForWORD#16#EA7F.
10. Verification Steps
- Force a known value (e.g.,
W#16#EA7F) into the source tag in the SINAMICS commissioning tool (STARTER, Startdrive, or Web server). - Online in the PLC, monitor the destination tag. Confirm the display reads 60031 (not -5505 or -5537).
- Trigger a fault on the drive (e.g., F07802 by enabling the drive with the line voltage below the threshold) and verify the fault code reads correctly on the HMI.
- Test the boundary: force
W#16#7FFF(32767) andW#16#8000(32768). WithINT, the second value will be -32768; withUINTorDINT, both will be positive. - If the value is to be shown on WinCC, verify the HMI tag type matches the PLC tag type. Mismatched types revert to negative display.
- For STL code on S7-300/400, toggle the IEC check in the source file properties to confirm the compiler accepts the cast. The compiler error is "IEC check: operation not allowed for this data type".
11. Quick-Reference Conversion Matrix
| Source Type | Target Type | Result for 0xEA7F | Recommended? |
|---|---|---|---|
| WORD (raw) | INT (direct assign) | -5505 (wraparound) | No |
| WORD (raw) | UINT (direct assign, S7-1200/1500) | 60031 | Yes, preferred |
| WORD (raw) | DINT via BTD | 60031 | Yes, for math |
| WORD (raw) | DINT via ITD | -5505 (sign-extended) | No, bug source |
| INT (after ITD bug) | DINT | -5505 | No, propagate error |
| UINT | REAL via UINT_TO_REAL | 60031.0 | Yes, for scaling |
| UINT | DINT via UINT_TO_DINT | 60031 | Yes, for math |
| UINT | INT via UINT_TO_INT (if range OK) | Compiler warning | Only if < 32768 |
FAQ
Why does W#16#EA7F show as a negative number in my S7-300?
Because the S7-300 does not have a native UINT type. Assigning the word to an INT tag forces 16-bit signed interpretation, and any value with bit 15 set (0x8000 or higher) wraps to negative. The fix is to use WORD, UINT (S7-1200/1500 only), or DINT with the BTD instruction.
What is the difference between ITD and BTD for unsigned 16-bit values?
ITD sign-extends a 16-bit INT to DINT, so 0xEA7F becomes -5505. BTD loads the 16 bits into the low word and zeros the high word, giving 0x0000EA7F = 60031. Use BTD for any unsigned 16-bit value from a drive.
How do I display a SINAMICS fault code (e.g., F07802 = 60031) on WinCC?
Declare both the PLC tag and the HMI tag as UINT (S7-1200/1500) or as DINT (S7-300/400). On classic S7-300 with STEP 7 V5, the HMI tag must be DINT; using INT will display the negative wraparound value.
Can I compare a WORD directly with a constant in SCL?
Only with the matching type prefix. Use IF myWord = WORD#16#EA7F or, on S7-1200/1500, IF myUint = UINT#16#EA7F. A bare integer constant in SCL defaults to INT and will be rejected by the compiler if the target is WORD.
Why does my SCL code compile with a warning about UINT to INT conversion?
TIA Portal emits a warning when an implicit narrowing conversion from UINT to INT could overflow. The compiled code still works, but if the runtime value exceeds 32767 the result wraps negative. Use UINT_TO_DINT instead and keep the value in 32-bit space for the calculation.
Is there a way to keep the value as 16 bits without using UINT on S7-300?
You can keep the tag as WORD and use only == and <> comparisons, or extract individual bits with A (AND) bit logic. For arithmetic or threshold comparisons, promote to DINT with BTD first. The "waste" of 16 bits is negligible on any modern S7 CPU.