S7-1200 to S7-1500 Heartbeat: PUT/GET Watchdog Configuration

David Krause15 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

S7-1200 to S7-1500 Heartbeat: PUT/GET Watchdog Configuration

A heartbeat between two Siemens S7-1200 and S7-1500 controllers is the lowest-cost, highest-reliability liveness check available on a Profinet/S7 LAN. The pattern is the same one used by Spectrum Controls gateway heartbeat tags and by every industrial protocol that supports a "deadman" (the controller, the SCADA, and the protocol bus itself all assume the peer will miss a cycle if it is no longer healthy). This reference shows how to design, code, configure, and verify a bidirectional heartbeat using only built-in PUT/GET instructions, the standard S7 connection resource, and the TIA Portal diagnostic buffer.

Scope. This document covers S7-1200 (CPU 1211C through CPU 1217C, firmware 4.2 to 4.6) and S7-1500 (CPU 1511 to CPU 1518, firmware 2.6 to 3.0). Where the behavior differs between families, both are called out explicitly. The same approach applies between two S7-1200s or two S7-1500s; only the instruction icon in TIA Portal differs.

1. Heartbeat Architecture

A heartbeat is a small, periodic, verifiable data exchange between two controllers. The receiver increments (or toggles) a value, the sender reads it back, and the receiver confirms it is seeing fresh data from the sender. The minimum useful heartbeat contains three pieces of information:

  1. A monotonic counter that increments every scan on the producer side. A non-incrementing counter is, by itself, proof that the producer is stuck, halted, or disconnected.
  2. A watchdog timeout on the consumer side. If the counter has not changed in N milliseconds, the consumer declares the link dead and executes the configured fail-safe routine.
  3. A mutual qualifier so that both sides know the other side has just consumed the most recent value. A simple 1-bit "I saw your counter last cycle" flag is sufficient.
S7-1200 (CPU 1215C) OB1 - Main HB_Counter_1200 := HB_Counter_1200 + 1 PUT (HB_Counter_1200 -> 1500) GET (HB_Counter_1500 <- 1500) Watchdog eval (HB_Counter_1500) S7-1500 (CPU 1515) OB1 - Main HB_Counter_1500 := HB_Counter_1500 + 1 PUT (HB_Counter_1500 -> 1200) GET (HB_Counter_1200 <- 1200) Watchdog eval (HB_Counter_1200) PUT GET Profinet / S7 connection

The key design decision is: do you need the heartbeat to advance every CPU scan, or only every Nth scan? PUT/GET instructions are not free; they consume connection resources and they trigger a partner-side update at the partner's cycle. If you set REQ every scan of a fast OB1, you will eventually starve the S7 connection queue on a CPU 1211C. If you set REQ only on a positive edge, the data you read back is the value the partner wrote at the time of its last successful PUT, not a fresh value.

2. Prerequisites

Item Minimum requirement Notes
CPU 121xC Firmware 4.2 or later Older firmware lacks the GET/PUT multi-instance support used here
CPU 151x Firmware 2.6 or later Firmware 2.9 recommended for OPC UA coexistence
TIA Portal V17 or later Required for the multi-instance PUT/GET DB pattern
S7 connection resource 1 PUT + 1 GET per direction Set in Devices & Networks > S7 connection
PUT/GET access right Permitted on partner S7-1500 CPU > Properties > Protection & Security > Connection mechanisms
Profinet interface Same subnet, reachable by TCP Port 102 must be open; check corporate firewall if routed
IP plan Static addresses, no DHCP Heartbeat is meaningless if the partner is at the wrong IP
Allow PUT/GET on the S7-1500. S7-1500 firmware 2.x and later ship with Permit access with PUT/GET communication disabled by default. Open the CPU device properties, navigate to Protection & Security > Connection mechanisms, and tick the box. Without it, the S7-1200 GET returns status 0x80C3 (object access denied). Reference: Siemens KB 109751799 - "Connection mechanisms of the S7-1500".

3. S7 Communication: PUT/GET Building Blocks

PUT and GET are the only instructions that need a configured S7 connection in TIA Portal. They are the lowest-level, lowest-overhead, highest-priority way to move a small block of memory between two S7 controllers on the same subnet. The full instruction set is documented in the S7-1500 system manual and the S7-1200 system manual.

Parameter PUT GET
REQ BOOL, edge-triggered. One transfer per rising edge. BOOL, edge-triggered. One read per rising edge.
ID WORD, the local connection ID from TIA Portal WORD, the local connection ID from TIA Portal
DONE BOOL, TRUE for one cycle on success BOOL, TRUE for one cycle on success
BUSY BOOL, TRUE while transfer in progress BOOL, TRUE while transfer in progress
ERROR BOOL, TRUE with STATUS valid BOOL, TRUE with STATUS valid
STATUS WORD, 0x0000 on success, error code on failure WORD, 0x0000 on success, error code on failure
ADDR_1 Remote target (write to) Remote source (read from)
SD_1 / RD_1 Local source (write from) Local destination (read into)

The standard error codes for both blocks are identical. The most important ones for a heartbeat are listed below; full enumeration is in the S7-1500 system manual.

STATUS (hex) Meaning Heartbeat action
0x0000 No error None
0x7000 No job active Trigger a new REQ
0x7001 Job started, BUSY=TRUE Wait
0x7002 Job running Wait
0x80A1 User data length error Fix ADDR_1 area pointer
0x80B0 No free connection resource Free a connection or reduce rate
0x80C3 Access denied (PUT/GET disabled) Enable on partner CPU
0x80C4 Partner not reachable Increment comm-fail counter, raise alarm
0x80D0 / 0x80D1 Partner CPU in STOP / cold start Hold last good heartbeat, alarm
0x80F0 Internal error Reboot, see diagnostic buffer

4. TIA Portal: Configuring the S7 Connection

  1. Open the project containing both CPUs. In Devices & Networks, drag from the S7-1200 Profinet port to the S7-1500 Profinet port. TIA Portal auto-creates an S7 connection with the local ID assigned to the source CPU.
  2. Select the S7 connection line. In the properties pane, set Connection type = S7 connection, Active connection establishment = local endpoint active, and confirm the partner IP.
  3. Open the S7-1200's Properties > Protection & Security > Connection mechanisms and confirm Permit access with PUT/GET communication from remote partners is ticked. Repeat on the S7-1500.
  4. Compile and download both stations. Verify the connection in the Online & Diagnostics view: it should show Established within 10 seconds.
  5. Create the heartbeat data blocks. On each CPU, create an Optimized block global DB named DB_HB_Local with the structure shown in Section 6.
Connection count. Each S7-1200 supports a maximum of 8 S7 connections for PUT/GET (firmware 4.4+ raises the limit; older firmware is 4 to 6). The S7-1500 supports 64 or more. Plan accordingly if the heartbeat shares the connection budget with HMI, SCADA, and other peer CPUs.

5. Heartbeat Tag Design

The smallest useful heartbeat payload is 12 bytes per side: 8 bytes for a DWord monotonic counter, 2 bytes for a watchdog timeout, and 2 bytes for status flags. In an optimized DB, the structure is symbolic and order-independent; in a standard DB the offset matters. Use a standard DB for PUT/GET because absolute addressing is mandatory for the remote ADDR_1 pointer.

5.1 Producer-side DB (non-optimized)

DATA_BLOCK "DB_HB_1200"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
NON_RETAIN
  STRUCT
   Counter  : DINT;   // Increments every scan, wraps at 2^31
   TimeoutMs : INT;   // 1500 typical for a 100 ms scan partner
   Status   : BYTE;   // 0x01 = RUN, 0x02 = partner-seen, 0x80 = local fault
   Spare    : ARRAY[0..4] OF BYTE;
  END_STRUCT;
END_DATA_BLOCK

5.2 Consumer-side DB (non-optimized)

DATA_BLOCK "DB_HB_Remote_1200"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
NON_RETAIN
  STRUCT
   LastCounter : DINT;   // Last value seen from partner
   DiffMs      : DINT;   // Milliseconds since last increment
   WatchdogOK  : BOOL;   // TRUE while partner is healthy
   Spare       : ARRAY[0..3] OF BYTE;
  END_STRUCT;
END_DATA_BLOCK

5.3 The counter wrap problem

A DINT counter wraps from 2 147 483 647 to -2 147 483 648. At 10 ms per increment that is roughly 24 days. Two practical mitigations:

  1. Compare (NewCounter - LastCounter) MOD 2^31. Any result other than 1 means the counter moved by more than one tick or wrapped; both are valid as long as the absolute difference is plausible.
  2. Reset the counter to 0 on cold restart, and require the consumer to accept the new value as authoritative for the next cycle. This is what the Siemens-recommended pattern does.

6. Implementing the Heartbeat in OB1

The simplest, most reliable implementation puts everything in OB1. The pattern below is the SCL version; the equivalent LAD uses a single network with an ADD box feeding the counter and a second network with the PUT block.

6.1 Producer logic (S7-1200, SCL)

// Network 1: increment local counter
IF "DB_HB_1200".Status <> 16#80 THEN
   "DB_HB_1200".Counter := "DB_HB_1200".Counter + 1;
END_IF;

// Network 2: send to S7-1500 every scan, on REQ rising edge
"Inst_PUT_to_1500"(REQ := "_ScanTick_50ms",
                   ID  := 1,
                   DONE=>"DB_HB_1200".DONE,
                   BUSY=>"DB_HB_1200".BUSY,
                   ERROR=>"DB_HB_1200".ERROR,
                   STATUS=>"DB_HB_1200".PUT_STATUS,
                   ADDR_1 := P#DB_HB_1500.DBX0.0 BYTE 12,
                   SD_1   := P#DB_HB_1200.DBX0.0 BYTE 12);

// Network 3: read back from S7-1500
"Inst_GET_from_1500"(REQ := "DB_HB_1200".DONE,
                   ID  := 2,
                   DONE=>"DB_HB_Remote_1500".DONE,
                   BUSY=>"DB_HB_Remote_1500".BUSY,
                   ERROR=>"DB_HB_Remote_1500".ERROR,
                   STATUS=>"DB_HB_Remote_1500".GET_STATUS,
                   ADDR_1 := P#DB_HB_1200_Remote.DBX0.0 BYTE 12,
                   RD_1   := P#DB_HB_Remote_1500.DBX0.0 BYTE 12);

"_ScanTick_50ms" is a clock bit generated by a periodic interrupt OB (OB30 to OB38) configured for 50 ms. Driving PUT/GET with a fixed-frequency tick instead of a free-running OB1 scan decouples the heartbeat from CPU load and gives a stable, known maximum latency on both sides.

6.2 Watchdog evaluation (S7-1200, SCL)

// Network 4: detect counter movement
IF "DB_HB_Remote_1500".LastCounter <> "DB_HB_Remote_1500".NewCounter THEN
   "DB_HB_Remote_1500".LastCounter := "DB_HB_Remote_1500".NewCounter;
   "DB_HB_Remote_1500".DiffMs := 0;
   "DB_HB_Remote_1500".WatchdogOK := TRUE;
ELSE
   "DB_HB_Remote_1500".DiffMs := "DB_HB_Remote_1500".DiffMs + "OB1_Scan_ms";
   IF "DB_HB_Remote_1500".DiffMs > "DB_HB_1200".TimeoutMs THEN
      "DB_HB_Remote_1500".WatchdogOK := FALSE;
   END_IF;
END_IF;

The symmetric block is duplicated on the S7-1500, swapping the connection IDs and the partner DB references.

7. Optimizing Scan Response with a Cyclic Interrupt OB

The OB1-only pattern above has one weakness: PUT/GET are non-preemptive. If a long-running FB blocks OB1, the heartbeat stalls. The Siemens-recommended fix is to move the REQ edge generation into a cyclic interrupt OB (OB30 to OB38) and let OB1 only toggle the trigger flag.

7.1 Why use a cyclic interrupt at all

Three reasons, in order of importance:

  1. Bounded latency. A cyclic interrupt OB is started by the CPU hardware timer; its start time is independent of OB1 duration. The maximum jitter is the cyclic period itself.
  2. Decoupling from heavy FBs. If OB1 is blocked for 200 ms by a recipe load, the cyclic interrupt still fires. The heartbeat will continue, and the operator will see the stalled OB1, not a confused comm error.
  3. Edge generation. PUT/GET consume one job per rising edge of REQ. Running them from a 50 ms cyclic interrupt gives a known, repeatable rate regardless of OB1 cycle time.

7.2 Configuration of OB30 (cyclic interrupt, 50 ms)

Property Value Notes
OB number OB30 Available on both S7-1200 and S7-1500
Phase offset 0 ms Set to 0 for the first cyclic interrupt
Cycle time 50 ms Configurable from 1 ms to 60 000 ms
Priority 9 (default) Lower than hardware interrupts (16+), higher than OB1 (1)
Watchdog time 200 ms 4x cycle to absorb one missed trigger

7.3 Body of OB30 (SCL, S7-1200 side)

// Network 1: rising-edge one-shot for PUT REQ
IF "_HB_Tick_Old" = FALSE AND "_HB_Tick" = TRUE THEN
   "_HB_PUT_REQ" := TRUE;
END_IF;
"_HB_Tick_Old" := "_HB_Tick";
"_HB_Tick" := TRUE;

// Network 2: PUT triggered
"Inst_PUT_to_1500"(REQ := "_HB_PUT_REQ",
                   ID  := 1,
                   DONE=>"_HB_PUT_Done",
                   BUSY=>"_HB_PUT_Busy",
                   ERROR=>"_HB_PUT_Err",
                   STATUS=>"_HB_PUT_Status",
                   ADDR_1 := P#DB_HB_1500.DBX0.0 BYTE 12,
                   SD_1   := P#DB_HB_1200.DBX0.0 BYTE 12);

// Network 3: clear REQ when finished (or when BUSY has stayed too long)
IF "_HB_PUT_Done" OR "_HB_PUT_Err" THEN
   "_HB_PUT_REQ" := FALSE;
END_IF;

// Network 4: GET, on PUT-DONE edge
IF "_HB_PUT_Done" THEN
   "_HB_GET_REQ" := TRUE;
END_IF;
"Inst_GET_from_1500"(REQ := "_HB_GET_REQ",
                   ID  := 2,
                   DONE=>"_HB_GET_Done",
                   BUSY=>"_HB_GET_Busy",
                   ERROR=>"_HB_GET_Err",
                   STATUS=>"_HB_GET_Status",
                   ADDR_1 := P#DB_HB_1200_Remote.DBX0.0 BYTE 12,
                   RD_1   := P#DB_HB_Remote_1500.DBX0.0 BYTE 12);
IF "_HB_GET_Done" OR "_HB_GET_Err" THEN
   "_HB_GET_REQ" := FALSE;
END_IF;
Do less work in the interrupt. Keep the cyclic interrupt body to PUT/GET plus edge handling. Counter increment, watchdog evaluation, alarm generation, and HMI flag updates all belong in OB1. The cyclic interrupt exists to deliver a known, stable request rate; processing belongs elsewhere.

8. Edge Cases and Failure Modes

8.1 Partner CPU in STOP

An S7-1500 in STOP keeps the S7 connection open and returns STATUS = 0x80D0 on GET. The counter does not advance. The watchdog times out at TimeoutMs (typical 1500 ms) and WatchdogOK goes FALSE. This is the correct behavior; do not treat ERROR = TRUE on GET as a connection failure, treat WatchdogOK = FALSE as the failure.

8.2 Partner unreachable (cable unplugged)

The local CPU keeps requesting. The first few requests return STATUS = 0x80C4 (partner not reachable). After the S7 keep-alive timeout (default 30 s, configurable in the S7 connection properties), the connection is torn down and PUT/GET return STATUS = 0x80B0 (no free connection resource) until the link is re-established. Reduce the keep-alive interval to 5 s for fast detection: open the S7 connection properties in TIA Portal and change Connection setup / monitoring.

8.3 OB1 scan longer than TimeoutMs

If OB1 takes longer than the watchdog timeout, the consumer will declare the producer dead while the producer is still incrementing correctly. The fix is to use the cycle time measurement (OB1 in the S7-1500 exposes OB1_PREV_CYCLE and OB1_CYCLE_TIME) and to set TimeoutMs to at least 3x the maximum measured OB1 cycle time on the producer side.

8.4 Multiple S7 connections sharing a single PUT/GET

The same PUT/GET instruction instance cannot be used for two partners; instantiate one PUT and one GET per partner. The S7-1200 connection budget is 8, so this is rarely a problem; on a CPU 1211C with several HMI tags it can be.

9. Profinet DeviceStates as a Lightweight Alternative

For systems where the only thing that matters is "is the partner CPU still on the network," the DeviceStates instruction (S7-1500) or the equivalent PNIO_ALARM blocks on the S7-1200 provide a hardware-driven answer without any application code. DeviceStates is documented in the S7-1500 system manual, chapter "Profinet diagnostics".

// S7-1500: check partner device state
"Inst_DeviceStates"(REQ := TRUE,
                    LADDR := 64,                // HW ID of the S7-1200 PN port
                    MODE := 1,                  // 1 = all devices
                    RET_VAL => "_DS_RetVal",
                    BUSY  => "_DS_Busy",
                    ERROR => "_DS_Err",
                    STATE => "_DS_State");
// _DS_State bit 0 = device 0 OK, 1 = device 0 not reachable, ...

DeviceStates is driven by the Profinet stack, not by application code, so it is immune to OB1 stalls. It cannot, however, detect a partner CPU that is in STOP (the Profinet link is still up). For full coverage, run DeviceStates and the PUT/GET heartbeat.

10. Diagnostic Buffer Analysis

When the heartbeat fails, the first place to look is the diagnostic buffer. From the S7 CPU's Online & Diagnostics view, sort by time descending and look for the following entries:

Event ID Meaning Heartbeat symptom
0x013C Communication error, partner not reachable Cable, switch, IP issue
0x015B S7 connection established Recovery event
0x0254 Connection terminated (local) PUT/GET returned 0x80B0
0x0282 Watchdog of cyclic interrupt elapsed Cyclic interrupt body too long
0x35xx Profinet IO fault Switch port down, partner CPU stopped

Detailed enumeration: Siemens KB 109751799 - Diagnostic buffer event IDs.

11. Performance and Tuning

Cycle time of OB30 PUT/GET load Recommended for
10 ms ~6% of S7 connection bandwidth per direction High-speed line, <50 ms safety response
50 ms ~1.5% per direction Default; covers 95% of installations
100 ms <1% per direction Many peers, HMI-heavy CPU
500 ms Negligible Process cells where liveness within 2 s is acceptable

The TimeoutMs should be at least 3x the OB30 cycle time and at least 3x the worst observed OB1 cycle time on the partner. A common value is 1500 ms with a 50 ms cyclic interrupt: 30x the cycle, ample margin for one or two missed ticks.

12. Verification and Commissioning Checklist

  1. Both stations are online; S7 connection shows Established in Online & Diagnostics.
  2. Monitor DB_HB_1200.Counter and DB_HB_1500.Counter in a watch table. Both must increment at the OB30 rate.
  3. Force a partner CPU STOP. Within TimeoutMs + one OB30 cycle, the local WatchdogOK must go FALSE.
  4. Restart the partner. Within one OB30 cycle after the partner comes back, both counters must resume incrementing and WatchdogOK must return TRUE.
  5. Disconnect the patch cable. The local PUT_STATUS must transition to 0x80C4 within one keep-alive interval, and WatchdogOK must go FALSE within 3 OB30 cycles of the new keep-alive interval (if the interval is shortened to 5 s).
  6. Reconnect. WatchdogOK must return TRUE without operator intervention.
  7. Check the diagnostic buffer. No recurring 0x0282 (cyclic interrupt watchdog) or 0x80B0 (no connection) entries should appear during normal operation.
Documentation handoff. Record the OB30 cycle time, the connection IDs, the partner IP, and the TimeoutMs in the function specification. Many field issues come from a replacement CPU being installed with a different firmware that defaults the PUT/GET access to disabled.

What is the minimum number of PUT/GET instructions for a bidirectional S7-1200/S7-1500 heartbeat?

Two: one PUT and one GET on each side, total four instructions. The PUT on the S7-1200 writes the local counter to the S7-1500; the GET on the S7-1200 reads the S7-1500's counter back. The reverse pair runs on the S7-1500. This gives each CPU its own monotonic counter and a watchdog on the partner's counter.

Why is my GET returning STATUS 0x80C3 even though the connection is established?

Access is denied. The S7-1500 default behavior on firmware 2.0 and later is to reject PUT/GET from any partner. Open the S7-1500 CPU's Properties > Protection & Security > Connection mechanisms and enable Permit access with PUT/GET communication from remote partners. Download the configuration and the GET will return 0x0000 within one cycle.

How fast should the OB30 cyclic interrupt run for a heartbeat?

50 ms is the recommended default. It keeps the PUT/GET load below 2% of the S7 connection bandwidth while giving a worst-case detection time of TimeoutMs + 50 ms. For safety circuits with sub-100 ms response, use 10 ms; for process cells where 1 s detection is fine, use 100 ms.

Can I use optimized DBs with PUT/GET between S7-1200 and S7-1500?

No. PUT/GET require absolute addresses in the ADDR_1 pointer, and absolute addressing is only guaranteed in non-optimized (standard) DBs. Create the heartbeat DBs with S7_Optimized_Access := 'FALSE' in the DB properties or in the source. All other process data can stay optimized.

My heartbeat works in OB1 but fails when I move it to a cyclic interrupt OB. Why?

Most commonly, the cyclic interrupt OB is firing faster than PUT/GET can complete (the BUSY bit is still TRUE when the next edge arrives). The PUT/GET instruction ignores re-triggers while BUSY is TRUE, so the request is simply lost. The fix is to gate the REQ input on the falling edge of BUSY, not on a free-running tick, or to slow the cyclic interrupt to a period longer than the worst PUT/GET response time (typically 30 to 50 ms over Profinet).

Back to blog