Resolving S7-1500 IO Module Diagnostics: HW_ID, LADDR and OB82

David Krause12 min read
S7-1200SiemensTechnical Reference
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

Overview

Engineers commissioning a SIMATIC S7-1500 frequently need a single Boolean output that says "module is faulty" for a given I/O logical address, e.g. input byte 123 or output word 256. The TIA Portal ships a fully featured diagnostic stack, but the documentation ladder starts at the IO system (IOSystem) hardware identifier and walks down through stations and slots. That top-down approach returns far more information than the application needs and inflates the code base of every project.

This reference compares the four production-ready paths to the same answer on a S7-1500 CPU (firmware V2.0 and later; instruction set extended on V2.5/V2.9):

  1. Event-driven OB82 Diagnostic Error Interrupt (zero polling overhead).
  2. RALRM inside OB82 for vendor-formatted channel diagnostics.
  3. Polling ModuleStates for a single subsystem (one call returns every slot).
  4. Address translation with LOG2MOD / GEO2LOG chained to ModuleStates when only the I/O logical address is known.

Each path is presented with the data structures it requires, the exact TIA Portal instruction signature, and a verification checklist. The article closes with a troubleshooting matrix for the most common commissioning defects.

Diagnostic Architecture in the S7-1500

The S7-1500 separates logical addresses (the %I / %Q range a program reads and writes) from hardware identifiers (HW_ID or LADDR, a 16-bit word used by system blocks and instructions). Every PROFINET device, PROFIBUS slave, sub-module and channel receives one HW_ID at compile time; the value is exposed as a system constant in the PLC tags table.

Term Symbol Length Source
Logical I/O address %I / %Q Byte/Word/DWord Configured I/O address in the device view
Hardware identifier HW_ID (LADDR) Word (UInt) System constants -> HW identifiers
Diagnostic address LADDR in OB82 Word (UInt) Operating system supplies at interrupt
IOSystem identifier HW_ID (system) Word (UInt) PROFINET IO system constant

Two key diagnostics OBs are wired automatically when the CPU is parameterised in TIA Portal:

  • OB 82 - Diagnostic Error Interrupt. Triggered by channel/module faults coming and going. Input tags: LADDR, IO_STATE, CHANNEL, MULTI_FAULT.
  • OB 83 - Remove/Insert Interrupt. Triggered on hot-swap of a module. Useful for line topology diagnostics but outside the scope of this article.
Field tip: OB 82 is called only while the fault is present and again when it clears. The second call (fault gone) is what tells you the channel is healthy again. Do not skip the cleared-event logic or your "Module_Fault" coil will latch on for the life of the project.

Identifying the Hardware Identifier from a Logical Address

If the application already has the HW_ID of the failing module (for example, the module was added to a user data type as a tag and the system constant is dragged into the program), skip this section. The problem most engineers have is the inverse: they know only the I/O address and want the slot, station and HW_ID.

The TIA Portal instruction set ships the following address-conversion blocks under Extended Instructions -> Addressing:

Instruction Direction Inputs Output Typical use
LOG2MOD Logical -> Module LADDR (log. byte), AREA (I/Q/PI/PQ) Module slot, station Find which slot holds input byte 123
GEO2LOG Geographic -> Logical HW_ID of station, slot, subslot LADDR Start from a known slot
LOG2GEO Logical -> Geographic LADDR (log. byte), AREA HW_ID of station, slot, subslot Reverse of GEO2LOG
RD_ADDR Any IO -> Pointer Symbolic tag Variant pointer Pass any tag to a generic FB

The combined call sequence that solves the original poster's requirement is shown below. The block returns the slot of the module that owns the logical address, which is then fed into ModuleStates for a station-wide status read.

// SCL - "GetModuleFaultFromLogAddr" FB
#i_LogAddr   := 123;             // input byte address
#i_Area      := 16#81;           // 0x81=Inputs (process image), 0x82=Outputs
#LOG2MOD    (LADDR := #i_LogAddr,
             AREA  := #i_Area,
             SLOT  => #statSlot,
             SUBSLOT=> #statSubSlot,
             ERR   => #statErr);
IF #statErr = 0 THEN
    // ModuleStates returns one byte per slot for the IO system
    #ModuleStates(LADDR     := "PROFINET_Interface_1".IOsystem_HWID,
                  STATE     := #statStates[0..31],
                  DIAG      := #statDiag[0..31],
                  RETVAL    => #statRetVal);
    #b_ModuleFault := #statStates[#statSlot] <> 0;
ELSE
    #b_ModuleFault := TRUE; // unknown mapping - assume worst case
END_IF;

The constants for PROFINET_Interface_1 are generated automatically when the PROFINET IO system is added; they appear under PLC tags -> System constants -> PROFINET IO system.

Approach 1: Event-Driven Fault Detection with OB82

OB 82 is the canonical Siemens mechanism. It carries four inputs that fully describe the event:

OB82 input Type Meaning
LADDR WORD HW_ID of the module that reported the fault
IO_STATE BYTE Bit-coded: bit 0 = good, bit 1 = fault, bit 2 = wrong module, bit 3 = channel fault, bit 4 = module removed
CHANNEL UINT Channel number (0 = whole module)
MULTI_FAULT BOOL TRUE if more than one fault is queued for this module

Because the inputs of OB 82 are valid for a single scan, copy them to a global diagnostic data block before processing. The skeleton below illustrates the recommended pattern: one SET rung per monitored module, plus a matching RESET rung that fires when the same module reports the event is cleared.

// OB82 Network 1 - capture inputs
#DiagDB.LADDR       := #LADDR;
#DiagDB.IO_STATE    := #IO_STATE;
#DiagDB.CHANNEL     := #CHANNEL;
#DiagDB.MULTI_FAULT := #MULTI_FAULT;
#DiagDB.EventTime   := RD_SYS_T; // SFC 1 returns current time-of-day

// OB82 Network 2 - fault SET per module
IF #LADDR = "Local_1_Slot_2".HWID THEN  // system constant
    IF (#IO_STATE AND 16#0E) <> 0 THEN  // bits 1, 2, 3 set
        "DiagDB".L1_Mod_Fault := TRUE;
    END_IF;
END_IF;

// OB82 Network 3 - fault CLEARED per module
IF #LADDR = "Local_1_Slot_2".HWID THEN
    IF (#IO_STATE AND 16#01) = 1 THEN   // bit 0 = good
        "DiagDB".L1_Mod_Fault := FALSE;
    END_IF;
END_IF;
Field tip: The IO_STATE bitmap changed between firmware V1.x and V2.x of the S7-1500. V1.x used bit 0 for fault and bit 1 for good; V2.x reverses that to the IEEE common model (bit 0 = good, bit 1 = fault). Confirm with the F1 help for your CPU's firmware version before shipping code.

After this OB executes, the program can consume "DiagDB".L1_Mod_Fault anywhere - in the HMI tag list, in WinCC Unified, in a safety logic input or in a maintenance flag visible on the CPU display.

Approach 2: Reading the Full Channel Record with RALRM

When the field engineer needs the specific channel that failed, not just the module, call RALRM (read alarm) inside OB 82. RALRM returns the complete vendor-formatted alarm record that the PROFINET device pushed into the CPU's diagnostic buffer.

// SCL - inside OB 82
#RALRM(OB      := 82,
       MODE    := 1,           // 0=read all, 1=read with filter
       LADDR   := #LADDR,      // filter on the module that just faulted
       TINFO   := #statTInfo,  // task information (input)
       AINFO   := #statAInfo,  // alarm information (output, vendor record)
       RETVAL  => #statRALRM_RetVal);

Decode the AINFO buffer against the GSDML of the slave (channel number, channel error type, channel error value, vendor extension). Siemens publishes a reference decoder in entry 98210758 ("S7-1500 - Diagnostics in the user program"). The block is on the Siemens support site under Automation Technology -> SIMATIC -> STEP 7 -> Sample projects.

Memory budget: AINFO is an ARRAY[*] OF BYTE. Size it generously (e.g. ARRAY[0..255] OF BYTE) to accommodate long records from complex devices. RALRM with a too-small target will return RETVAL 16#80C1.

Approach 3: Polling ModuleStates

ModuleStates reads the operational state of every module and sub-module in a given IO system with a single call. The function returns two byte arrays:

  • STATE[x] - 0 = OK, 1 = faulty, 2 = wrong module, 3 = sub-module mismatch, 4 = slot not configured.
  • DIAG[x] - bit 0 = channel 0 fault, bit 1 = channel 1 fault, etc.
// SCL - periodic poll from a cyclic OB (e.g. OB 30, OB 35)
#ModuleStates(LADDR  := "PN_IO_System_1".IOsystem_HWID,
              STATE  := #statState[0..31],
              DIAG   := #statDiag[0..31],
              RETVAL => #statRetVal);

FOR #i := 0 TO 31 DO
    IF #statState[#i] = 1 THEN
        "DiagDB".Slot[#i].Fault := TRUE;
    ELSE
        "DiagDB".Slot[#i].Fault := FALSE;
    END_IF;
END_FOR;

ModuleStates is event-safe: it does not race OB 82, and it can be called from any priority level. Call it from a slow cyclic OB (OB 30, 1 s; OB 35, 100 ms) to refresh an HMI overview screen without flooding the bus.

RETVAL Cause Action
16#0000 OK Continue
16#80A1 IO system not configured Check PROFINET IO system in device view
16#80B1 HW_ID does not exist Refresh system constants (compile)
16#80C1 Output buffer too small Increase length of STATE/DIAG arrays
16#80C3 Internal resource busy Re-call next cycle

Approach 4: From Logical Address to ModuleState (the Field-Proven Recipe)

The original poster's production requirement is satisfied with a three-step ladder:

  1. Translate the logical I/O address to a slot number using LOG2MOD.
  2. Read the entire IO system once with ModuleStates.
  3. Index into the state array with the slot number from step 1.

This is the minimum code path that gives a single Boolean per arbitrary logical address. The same FB can be instantiated for every module the application wants to supervise, and it scales with no additional code: just change the input address.

User program LOG2MODLADDR + AREA -> SLOT ModuleStatesHW_ID -> STATE[0..n] Index STATE[SLOT]0 = OK / 1 = FAULT BOOL output STATE[]

The flow above reads a logical I/O address, converts it to a slot number, polls the entire PROFINET station, and indexes the state array to produce a single Boolean. With LOG2MOD + ModuleStates the entire diagnostic primitive lives in a single FB that can be reused across the project.

GEN_DIAG: Producing Vendor-Style Diagnostics for Third-Party Devices

Some third-party PROFINET devices do not push their own diagnostic records. Use GEN_DIAG to synthesise a diagnostic record in the CPU that the HMI and TIA Portal can interpret just like a Siemens-original record. The instruction is part of the S7-1500 extended instruction set and accepts a GEN_DIAG_DB structured data block plus a DIAG_RECORD Variant.

Typical use cases:

  • Mapping a PLC-detected soft error (e.g. a calculated value out of range) to a channel fault on a dummy slot.
  • Surfacing a HART device warning that the GSDML suppresses.
  • Converting a CPU-internal fault (e.g. firmware watchdog) to a PROFINET-readable channel error.

For full signatures and an example application see the TIA Portal help: GEN_DIAG: Generate diagnostics information (S7-1500).

Reading the Same Information on the CPU Display

When the TIA Portal is not available, navigate the S7-1500 front display:

  1. Select Module on the home screen.
  2. Open PROFINET I/O (X1).
  3. Select the Station.
  4. Select the Slot.
  5. Open Status -> Module status.

The display shows the same value ModuleStates would return to the user program. Use the display to cross-verify that the BOOL you wired in the program matches what the CPU sees on the wire. Reference: Diagnostics via the display of the S7-1500 CPUs.

Performance and Sizing Notes

Approach CPU load Bus load Reaction time Recommended use
OB 82 + IO_STATE decode Negligible None (event) < 1 ms from event to program Safety / fast reaction
OB 82 + RALRM Moderate per call None < 1 ms from event to record Channel-level diagnostics
ModuleStates poll Constant cyclic None One OB cycle (e.g. 100 ms) HMI overviews, dashboards
LOG2MOD + ModuleStates Same as poll None One OB cycle Per-tag Boolean from logical address
GEN_DIAG Per call None Immediate Soft-error mapping
Workload rule of thumb: Do not call ModuleStates more than once per scan of the cyclic OB. A 100 ms OB 35 is appropriate for dashboards. Faster rates buy nothing, because the diagnostic buffer in the slave is updated by the device itself, not by polling.

Verification Procedure

  1. Compile the project and check the System constants tab: every expected module and IO system must have a generated HW_ID. Missing constants indicate a misconfigured device view.
  2. Download to the S7-1500 and go online. Add the diagnostic DB to a Monitor / Modify watch table.
  3. Force a wire break on a known channel (e.g. remove the field wire on a DI module's first input). OB 82 must fire within one PROFINET update cycle (typically 4 ms).
  4. Verify the diagnostic DB tag L1_Mod_Fault goes TRUE. Note the IO_STATE value and confirm it matches the bits documented above for your firmware version.
  5. Reconnect the wire. OB 82 must fire a second time with the cleared-event state. The fault tag must reset.
  6. On the CPU display, navigate to the same module. The Module status page must read OK.
  7. Run the Online & Diagnostics -> PROFINET diagnostics view in TIA Portal; the same module must show no error.

If any of the above steps disagree, see the troubleshooting matrix below.

Troubleshooting Matrix

Symptom Likely cause Correction
OB 82 never fires despite wire break Channel diagnostics disabled at the device Device view -> Module parameters -> Diagnostics -> enable Channel diagnostics
OB 82 fires but LADDR = 0 Module added to hardware catalogue but not inserted in the slot Check the configured assignment table and re-download
ModuleStates RETVAL = 16#80A1 Wrong HW_ID passed (likely the device HW_ID, not the IO system HW_ID) Use the IO system system constant, not the station constant
LOG2MOD returns SLOT = 0, ERR = 1 Address is in the process image partition that the address is in (constant area) Confirm the address type in the PLC tag table; pass AREA = 0x81 (input) or 0x82 (output)
STATE array too short Slaves with more slots than the declared array Increase STATE/DIAG array length, regenerate
Fault tag latches on after the event clears Only the SET branch of OB 82 is implemented Add the CLEARED branch on bit 0 of IO_STATE
GEN_DIAG produces RETVAL 16#80C3 Target slot already holds a vendor record Clear the slot with CLR_DIAG before re-generating

Frequently Asked Questions

Which is faster for module diagnostics on S7-1500, OB82 or ModuleStates?

OB82 is event-driven and reacts within one PROFINET update cycle (typically 1-4 ms). ModuleStates is polled, so its reaction time is bounded by the calling OB (e.g. 100 ms with OB35). Use OB82 for safety and motion; use ModuleStates for HMI overviews and dashboards.

How do I find the hardware identifier (HW_ID) of a module from a logical I/O address?

Use the LOG2MOD instruction under Extended Instructions -> Addressing. Pass LADDR (the I/O byte) and AREA (0x81 for inputs, 0x82 for outputs) to get SLOT and SUBSLOT. Combine with GEO2LOG or LOG2GEO for the full geographic mapping when needed.

Why does my OB82 fault coil stay TRUE after the wire break is fixed?

OB82 is called twice for every event: once on fault and once on cleared. If you only handle the SET case, the coil will latch on. Add a second network that resets the coil when IO_STATE bit 0 is set (module is good).

Can I use ModuleStates with the PROFINET IO system HW_ID, or do I need the device HW_ID?

ModuleStates expects the IO system HW_ID (the parent), not the device HW_ID. The instruction returns the state of every module and sub-module within that IO system in one call. Passing a device HW_ID returns RETVAL 16#80A1.

How do I generate a PROFINET-style diagnostic record from inside the user program for a third-party device?

Use the GEN_DIAG instruction (S7-1500 extended instructions). It accepts a GEN_DIAG_DB structure and a DIAG_RECORD Variant, and pushes a record that WinCC Unified and TIA Portal interpret like a vendor record. See the TIA Portal help for signatures and example code.

Does LOG2MOD work for both inputs and outputs?

Yes. Set the AREA parameter to 0x81 for the process input image, 0x82 for the process output image, 0x01 for direct inputs (PI) and 0x02 for direct outputs (PQ). Mismatching AREA returns ERR = 1 and SLOT = 0.

Back to blog