Overview of Siemens S7 Communication Paths for AdvancedHMI
AdvancedHMI is a Visual Studio-hosted HMI framework that ships with strong native drivers for Allen-Bradley (DF1, EtherNet/IP), Modbus TCP/RTU, and a number of smaller automation vendor protocols, but it does not ship with a built-in Siemens S7 driver. Engineers integrating an S7-200, S7-300, S7-400, S7-1200, or S7-1500 controller against an AdvancedHMI front end therefore have to select one of three integration paths:
- A direct driver wrapper written around libnodave, the open-source C library originally developed by Thomas Valesky. This path works on S7-200 (PPI over Ethernet via CP243-1), S7-300/400 (MPI/ISO-on-TCP), and the S7-1200/1500 with caveats.
- A wrapper around Snap7, the actively maintained C++/C#/.NET client library by Davide Nardella that natively speaks S7 communication.
- An OPC / OPC UA server acting as a bridge. Siemens offers SIMATIC NET OPC servers, and third-party servers such as Kepware KEPServerEX, Softing's S7 OPC, or the open-source opc-ua-s7 bridge expose S7 tags through a standardized interface that AdvancedHMI can consume through its OPC client plugin.
The decision between these paths is driven primarily by controller firmware (S7-1200 V4+ blocks the legacy S7 communication used by libnodave), available connection resources on the CPU (PG/OP/Route/HMI), and the operational risk tolerance of running a Windows service that brokers OPC traffic.
Siemens S7 Communication Stack and Connection Limit Constraints
Every S7 CPU has a hard ceiling on the number of simultaneous S7 communication partners it will accept. The published budget is divided into PG, OP, S7 (basic S7 communication), and Route connections. Crossing the limit yields a CPU-side Wireshark trace with ISO-on-TCP CR packets that the PLC refuses with a DT T-disconnect, and on the HMI side a 0x8104 or "Resource exhausted" return code from the driver.
| CPU | Max PG | Max OP | Max S7 | Max Route | Total S7 Connections |
|---|---|---|---|---|---|
| S7-200 (Ethernet via CP243-1) | 1 | 0 (no OP) | 8 | n/a | 8 |
| S7-300 (e.g., 315-2 PN/DP) | 4 | 4 | 16 | 4 | 16 |
| S7-400 (e.g., 416-3) | 8 | 8 | 32 | 16 | 32 |
| S7-1200 (Firmware V4.x) | 3 | 8 | 8 | 4 | Up to 16 |
| S7-1500 | 4 | 16 | 16 | 8 | Up to 32 |
For firmware-specific slot-by-slot limits refer to the Siemens SIMATIC S7-1500 manual set and the S7-1200 programmable controller system manual.
Library Option 1 — libnodave with AdvancedHMI
libnodave was released by Thomas Valesky as a C library that speaks ISO-on-TCP (RFC 1006), MPI over TCP, and PPI. Official project documentation is hosted on libnodave.sourceforge.net. The protocol library exposes a small API:
| Function | Purpose |
|---|---|
daveNewConnection() |
Create a TCP/MPI socket to the PLC at IP/port 102. |
daveConnectPLC() |
Negotiate the S7 communication session. |
daveReadBytes() |
Read DB/M/I/Q/PI/PQ/PE/PA memory areas. |
daveWriteBytes() |
Write DB/M/I/Q/PI/PQ/PE/PA memory areas. |
davePrepareReadBits() / daveExecRead()
|
Batch multiple bit-area reads in one transaction. |
daveGetError() |
Return error string after a failure. |
To bind libnodave to AdvancedHMI you write a managed wrapper that loads the compiled DLL and exposes its functionality through a CommunicationInterface-derived class. The AdvancedHMI driver model expects two entry points: Read() and Write() against a string Key that ends with a numeric offset (e.g., DB100.DBD0, MW10).
Wrapper Skeleton (C#)
using System;
using System.Runtime.InteropServices;
using AdvancedHMIDriverCommons;
using libnodave; // imported class library
namespace AdvHMI.Siemens
{
public class SiemensTcpDriver : EthernetPLCBase
{
private daveConn _conn;
public override void Connect()
{
int rack = Properties.Settings.Default.Rack;
int slot = Properties.Settings.Default.Slot;
int fds = libnodave.openSocket(
Properties.Settings.Default.IPAddress,
102);
_conn = new daveConn(
libnodave.daveNewConnection(fds, 0, 0, rack, slot));
int rc = libnodave.daveConnectPLC(_conn);
if (rc != 0)
throw new System.IO.IOException(
"S7 connect failed, libnodave rc=" + rc + " : " +
libnodave.daveGetError(rc));
}
public override short Read(string key)
{
// key like "MW10" or "DB100.DBB0"
Area area; int dbNum; int byteStart; int bit;
if (!libnodave.parseKey(key, out area, out dbNum,
out byteStart, out bit))
throw new ArgumentException("Bad key: " + key);
byte[] buf = new byte[2];
int rc = libnodave.daveReadBytes(_conn, area,
dbNum, byteStart, 2, buf);
if (rc != 0) throw new IOException(
"libnodave read err " + rc);
return (short)((buf[0] << 8) | buf[1]);
}
public override void Write(string key, short value)
{
Area area; int dbNum; int byteStart; int bit;
if (!libnodave.parseKey(key, out area, out dbNum,
out byteStart, out bit))
throw new ArgumentException("Bad key: " + key);
byte[] buf = BitConverter.GetBytes(value);
int rc = libnodave.daveWriteBytes(_conn, area,
dbNum, byteStart, 2, buf);
if (rc != 0) throw new IOException(
"libnodave write err " + rc);
}
}
}
Connecting to Specific CPU Families
| CPU | Transport | Rack / Slot (TSAP) | libnodave Result Code Map |
|---|---|---|---|
| S7-300 | ISO-on-TCP port 102 | Rack 0, Slot 2 typical | 0x0101 connect-ok, 0x8104 rejected, 0xFFFC CPU busy |
| S7-400 | ISO-on-TCP port 102 | Rack 0, Slot 3 typical | same as S7-300 |
| S7-200 via CP243-1 | ISO-on-TCP port 102 | Rack 0, Slot 0 or 1 | same plus 0x0203 if "connect to active partner" not enabled |
| S7-1200 V4+ | ISO-on-TCP port 102 — but PUT/GET disabled by default | Rack 0, Slot 1 | 0x8104 or "ISO: Connection refused" until PUT/GET permitted |
S7-1200/1500 PUT/GET Enablement
libnodave relies on the legacy S7 PUT/GET service, which Siemens disabled by default starting with S7-1200 firmware V4.0. The PLC will reject every connection request with a connection-fault error until the option is enabled. From TIA Portal:
- Open the PLC's Device Configuration.
- Select the CPU and open Properties > General > Protection & Security (for S7-1500) or Properties > General > Access Control (for S7-1200 V4).
- Under Connection Mechanisms, check Permit access with PUT/GET communication from remote partner.
- Re-compile and download the hardware configuration.
Known libnodave Tag-Address Limitation
The reference implementation of the libnodave AdvancedHMI wrapper distributed in early community samples only resolves bit-area addresses M0.0 through M0.7. Bytes M1.x and M2.x return an out-of-range parser error and an ArgumentException on read. The fix is to update the parseKey() routine so that it accepts multi-byte M-area offsets and returns the byte index plus optional bit position. Verified patch scope: M0.0 to M2.7 inclusive. Beyond M2.7 the parser is fine; the PLC response is unchanged.
Library Option 2 — Snap7 Wrapper for AdvancedHMI
Snap7 (hosted at snap7.sourceforge.net) is a portable IEC 61131-3-aware client that wraps the S7 protocol into a coherent set of S7Client, S7Server, and S7Partner classes. It is the modern replacement for libnodave. The .NET binding snap7.net drops straight into AdvancedHMI because AdvancedHMI projects are .NET Framework 4.x assemblies.
Snap7 .NET Wrapper Skeleton
using Snap7;
using AdvancedHMIDriverCommons;
namespace AdvHMI.Siemens.Snap7
{
public class Snap7Driver : EthernetPLCBase
{
private readonly S7Client _client = new S7Client();
private S7Client.S7DataItem[] _batch;
public override void Connect()
{
int rc = _client.ConnectTo(
Properties.Settings.Default.IPAddress,
rack: Properties.Settings.Default.Rack,
slot: Properties.Settings.Default.Slot);
if (rc != 0)
throw new System.IO.IOException(
"Snap7 connect failed: " + _client.Text(rc));
}
public override short Read(string key)
{
// Examples:
// "MW10" → Area MK, Length 2
// "DB100.DBD0" → Area DB, DB 100, Length 4
var area = Parse(key);
byte[] buf = new byte[4];
int rc = _client.ReadArea(area.Area, area.DB,
area.Start, area.Length,
buf);
if (rc != 0) throw new IOException(
"Snap7 read err " + _client.Text(rc));
return (short)((buf[0] << 8) | buf[1]);
}
public override void Write(string key, short value)
{
var area = Parse(key);
byte[] buf = BitConverter.GetBytes(value);
int rc = _client.WriteArea(area.Area, area.DB,
area.Start, area.Length,
buf);
if (rc != 0) throw new IOException(
"Snap7 write err " + _client.Text(rc));
}
}
}
Snap7 Connection Parameters and Error Codes
| Snap7 Result Code | CPU Code | Meaning | Correction |
|---|---|---|---|
0x00000000 |
— | CPU returned no error | none |
0x00000001 |
0xFFFE | CPU fault / system error | Inspect CPU diagnostics buffer |
0x00190002 |
0x8104 | Connection refused | Enable PUT/GET, check Rack/Slot |
0x00000010 |
— | Invalid area | Check DB exists, PLC is in RUN, no compile error |
0xFFFF0110 |
— | ISO: Invalid TP header | Bad TSAP or rack/slot mismatch |
0xFFFF0115 |
— | TCP disconnect by remote | Network path, ACL, firewall |
Snap7 PDU Sizing
Snap7 negotiates the maximum PDU length at connect time (typically 960 bytes for S7-300/400, 480 bytes for S7-1200/1500 firmware V4+). Larger PDUs mean fewer round trips for batched reads. The companion S7Client.SetPDULength() call forces a renegotiate; leave at 0 to let Snap7 determine the maximum accepted.
OPC Server Intermediary Approach
Where a fully custom driver is impractical — multi-protocol sites, regulated environments, or shops that already operate an OPC server for MES/ERP integration — bridging through OPC is the lower-risk path. AdvancedHMI ships an OPC client plug-in that consumes any OPC DA 2.0/3.0 server. The standard servers that expose Siemens S7 are:
| Server | Vendor | Bridges To | License |
|---|---|---|---|
| SIMATIC NET OPC Server | Siemens | S7-200/300/400, S7-1200/1500 | Bundled in SIMATIC NET |
| KEPServerEX S7 Ethernet Driver | PTC/Kepware | S7-200/300/400 (Siemens, Microwin); S7-1200/1500 via +PLUS | Commercial, per-CPU |
| Softing S7 OPC Server | Softing | S7-300/400, S7-1200/1500 | Commercial |
| opcua-s7-bridge (open source) | open source | S7-1500 TCP | GPL/MIT |
The decision has implications:
- Single point of consumption of S7 connections (one socket per CPU).
- Tag browsing in AdvancedHMI uses the OPC browser, not S7-area raw notation.
- Tag updates are async via OPC callbacks — AdvancedHMI uses subscription-based polling internally.
- DCOM becomes a configuration surface (see below).
OPC Bridge Sequence of Operations
- OPC server service starts on a Windows host (typically the engineering server).
- OPC server holds one ISO-on-TCP socket per PLC; on connect it negotiates the S7 PDU length and caches the negotiated value across reconnects.
- AdvancedHMI OPC client enumerates server tags via the
IOPCServer::Browse()interface. - AdvancedHMI subscribes to tags via
IOPCAsyncIO2::ReadMaxAge()with a polling rate matching the subscribed refresh interval (typical 100–500 ms per group). - Each subscribed update is delivered to AdvancedHMI through the
OnDataChangecallback, which pushes the value into the matchingBasicValueDisplay,PIDFaceplate, orDataGridViewtag instance.
DCOM Configuration for OPC Servers
OPC DA servers run as either out-of-proc COM objects or as Windows services. When AdvancedHMI sits on a different host than the OPC server (or the OPC server runs under a Windows service account), DCOM authentication and launch/activation permissions govern whether the client can reach the server. The known failure modes are clustered around five Windows nodes:
| Node | Required Setting | Symptom of Misconfiguration |
|---|---|---|
| Component Services > Computers > My Computer > DCOM Config > <Server ProgID> | Authentication Level = Connect or Default |
0x80070005 Access Denied on CoCreateInstanceEx
|
| Same node > Security tab | Launch/Activation: grant "Local Launch", "Remote Launch", "Local Activation", "Remote Activation" to client user/account |
0x80080005 Server execution failed |
| Same node > Identity tab | "This user" pointing to a service account with read on S7-Network, or "Interactive User" for local HMIs | Server starts but disconnects under session isolation |
| My Computer > COM Security > Access Permissions | Allow anonymous logon and the matching user/group |
0x80070005 under "Default Access Permission" |
| Windows Firewall | Inbound rule allowing %SystemRoot%\System32\dllhost.exe (or the OPC server service executable) and DCOM port range | Connection hangs at IRundown::Connect
|
OPC Server Hardening for Production
- Run the OPC server as a dedicated Active Directory service account, not as SYSTEM, so DCOM audit logs identify the OPC user.
- Set Default Authentication Level for Machine to Connect across both the OPC server host and the AdvancedHMI host.
- Use a fixed TCP port for the OPC server's remote connection (DCOM defaults to a dynamic port range above 49152). Lock the firewall to that static port range.
- On the AdvancedHMI host, add the OPC server hostname to the Trusted Sites and Local Intranet zones if the OPC server publishes a remote access XML bridge.
HKLM\Software\Microsoft\Rpc\ClientProtocols\NamedPipe and Netbios priorities, and explicitly set Default ResolutionMode to "Default" in Component Services > Computers > My Computer if the OPC server is older.Tag Addressing Reference
Tag syntax differs between the raw-driver path and the OPC path. Use this mapping to design tag archives:
| S7 Notation | Meaning | AdvancedHMI raw Driver String | KEPServerEX/SIMATIC NET OPC Browser Path |
|---|---|---|---|
| I0.0 | Discrete input, byte 0 bit 0 | I0.0 |
S7-1200 PLC_1.Inputs.I0_0 |
| Q0.7 | Discrete output, byte 0 bit 7 | Q0.7 |
S7-1200 PLC_1.Outputs.Q0_7 |
| MW10 | Merker word at byte 10 | MW10 |
S7-1200 PLC_1.Merker.MW10 |
| M0.7 | Merker bit, byte 0 bit 7 | M0.7 |
S7-1200 PLC_1.Merker.M0_7 |
| DB100.DBD0 | DB100, double word at byte 0 | DB100.DBD0 |
S7-1200 PLC_1.DataBlocks.DB100.DBD0 |
| DB100.DBX0.0 | DB100, bit at byte 0 bit 0 | DB100.DBX0.0 |
S7-1200 PLC_1.DataBlocks.DB100.DBX0_0 |
| PIW256 | Peripheral input word at 256 | PIW256 |
S7-1200 PLC_1.Inputs.PIW256 |
| PQW512 | Peripheral output word at 512 | PQW512 |
S7-1200 PLC_1.Outputs.PQW512 |
Hardware and Firmware Verification
Before commissioning, verify the PLC, the network, and the Windows host simultaneously. A connection failure can be at any of these layers:
- PLC side: Open TIA Portal, online > accessible nodes, and confirm that the PLC responds on TCP 102. Note the firmware version (S7-1200 V4+ and S7-1500 require PUT/GET enablement).
-
Network:
ping <PLC-IP>from the AdvancedHMI host;tcping <PLC-IP> 102(or PowerShellTest-NetConnection -Port 102) to verify the TCP path. -
Windows host: open PowerShell as Administrator and check the firewall with
Get-NetFirewallRule | Where DisplayName -like "*Siemens*". If a deny rule exists, scope it before troubleshooting the PLC. -
Driver test: once Snap7 or libnodave is bound into AdvancedHMI, add a
BasicValueDisplaylinked toDB1.DBD0and aPIDFaceplatelinked toDB1.DBD4. Both should bind to the AdvancedHMI tag database on save.
Performance, Polling, and Connection Sizing
The wire-level traffic is dominated by polling. Each AdvancedHMI tag in poll mode becomes one S7 read request, unless the driver is configured to group reads by area, in which case one request can carry up to (PDU-length / 19) variable items.
| PDU Length (bytes) | Variable Items / Read | Optimal for |
|---|---|---|
| 240 | ~12 | S7-200 |
| 480 | ~25 | S7-1200/1500 default |
| 960 | ~50 | S7-300/400 max |
For a tag count N with average poll rate R Hz per tag, the full-cycle read consumes:
WireRate (bps) = N * R * ( Request(19B) + Response(19B + payload) )
For a flat 100-tag screen refreshing at 250 ms, with average payload of 4 bytes per tag, the line carries:
WireRate = 100 * 4 * (19 + 23) = 16,800 B/s ~= 135 kbps
This is negligible against a 100 Mbps Ethernet, but the CPU-side stress at the OB1 cyclic update ticks must still be considered: every read of an Input/Output/DB area forces the CPU's communication task to serialize the response with the scan. For S7-1200/1500, keep total S7 polling load under 5% of the scan time budget to leave headroom for PG, OP, and route connections.
Troubleshooting Matrix
| Symptom | Most Likely Cause | First Diagnostic Step | Corrective Action |
|---|---|---|---|
| Connection refused on TCP 102 | S7-1200/1500 PUT/GET disabled | Open TIA Portal > Properties > Protection | Enable "Permit access with PUT/GET" |
| libnodave rc 0x0101 then 0x8104 | CPU rejected because connection table full | Check CPU diagnostic buffer for "max PG/OP reached" | Reduce number of S7 clients or close inactive sessions |
| Snap7 rc 0xFFFF0115 | TCP closed by remote (firewall reset) | Inspect intermediate firewall and switch | Allow ISO-on-TCP TCP 102 path; verify ACLs |
OPC 0x80070005 Access Denied |
DCOM launch/activation permission missing | Component Services > Security tab | Grant "Remote Launch + Remote Activation" to OPC client user |
| Tag values are stale | OPC subscription default refresh set too low | Inspect group's UpdateRate | Set UpdateRate = 100 ms, Deadband = 0 |
| M area addresses M1.x, M2.x throw ArgumentException | Original libnodave AdvancedHMI wrapper parser limitation | Look at parseKey() boundary code | Extend parser to multi-byte M offsets |
| Disconnect after 60s idle | Keep-alive disabled on HMI host | Wireshark observe FIN after 60s | Enable TCP keep-alive or implement keep-alive S7 request every 10–20 s |
| Browses tags in OPC but quality = Bad | Server cannot reach PLC | Open the OPC server's diagnostics | Address the underlying S7 reachability issue first |
| Reads return zeroes after writes | Optimized DB access in S7-1500 prevents unconfirmed-bit read | Check DB properties in TIA Portal | Disable "Optimized Block Access" on the DB being read |
Recommended Solution Pattern by Plant Profile
| Plant Profile | Recommended Path | Rationale |
|---|---|---|
| Single S7-300 line, 10–50 tags, no existing OPC | libnodave advanced-hmi wrapper | Lowest deploy cost; zero server footprint |
| Multi-controller line with existing MES/OPC | OPC bridge (KEPServerEX or SIMATIC NET) | Single point of consumption, layered architecture |
| Brownfield S7-1200/1500 plant, IT-secured | Snap7 with PUT/GET enabled and IP-restricted VLAN | Modern library, no DCOM surface area |
| Regulated environment (FDA/GxP) | OPC server with audit trail | Auditable change of values, no custom DLL |
| Mixed Allen-Bradley + Siemens shop | AdvancedHMI running against both drivers | One engineering environment, two drivers |
Migration Notes From libnodave to Snap7
Engineers supporting deployed libnodave-based integrations should plan a migration when the customer requires S7-1200 V4+ or S7-1500 firmware V2.5+ where the libnodave parser is brittle. The migration generally follows these data-flow equivalences:
-
daveConnectPLC()→S7Client.ConnectTo(ip, rack, slot) -
daveReadBytes(conn, area, db, start, len, buf)→S7Client.ReadArea(areaCode, db, start, len, buf) -
daveWriteBytes(conn, area, db, start, len, buf)→S7Client.WriteArea(areaCode, db, start, len, buf) -
daveGetError(rc)→S7Client.Text(rc)
Address syntax M0.7 remains identical because both libraries speak the underlying S7 protocol; only the wrapper signatures change. This makes it practical to provide a parallel Snap7 driver during the proof of concept and retire the libnodave driver once the NewTek-style work is accepted.
Safety and Security Caveats
- Never expose TCP 102 to the corporate public network. S7 is a clear-text, unauthenticated-by-default protocol and is the common vector for ICS/SCADA malware as documented in industrial cyber-incident reports published by CISA.
- When enabling PUT/GET on an S7-1200/1500, restrict the connection at the firewall level to the known HMI/server hosts. Do not rely on PUT/GET alone for traffic authorization.
- Lock the AdvancedHMI host with strong user ACLs so the OPC client cannot be redirected to an attacker-controlled server.
- Where write-from-HMI is required for operator actions (start/stop, setpoint), implement a separate confirmation token handshake inside the PLC before the write executes.
Verification Checklist Before Site Acceptance
- Confirm
tcping <PLC-IP> 102succeeds from the HMI host with a sustained < 5 ms latency. - Confirm every pollable tag rendered in AdvancedHMI displays its expected value within 2 update cycles.
- Execute a forced write-back test (operator sets a setpoint to mid-range and verifies the S7 tag updates).
- Force a CPU STOP/RUN cycle and observe the driver reconnect automatically (AdvancedHMI internal timers should perform a back-off retry between 2 s and 30 s).
- Disable the OPC server service and observe the documented error path in the AdvancedHMI status panel; restore service and confirm re-sync.
- Pull a Wireshark capture during a 60-second idle interval to verify periodic keep-alive is on the wire and no rogue TCP 102 traffic appears from other hosts.
Can AdvancedHMI talk directly to an S7-1200/1500 without an OPC server?
Yes, but only when PUT/GET communication is enabled in the CPU's protection settings and the driver wrapper uses Snap7 (recommended) or a patched libnodave. Drivers built against legacy libnodave typically fail on S7-1200 firmware V4.x and S7-1500 firmware V2.0+ with result code 0x8104 until PUT/GET is permitted.
Why does my M-area address above M0.7 fail with ArgumentException?
The reference libnodave-AdvancedHMI parser bundled in many community samples only accepts Mx.y where x = 0. You must extend the parseKey routine to accept multi-byte offsets. After the patch, addresses M1.0 to M2.7 and beyond resolve correctly because libnodave itself supports them; the limitation is purely in the C# wrapper.
How many AdvancedHMI clients can share one S7 CPU?
That is bounded by the CPU's OP and S7 connection resource counters, not by AdvancedHMI itself. S7-300 typically allows 4 OP + 16 S7 partners (16 total), while S7-1500 allows 16 OP + 16 S7 (32 total). Each AdvancedHMI instance consumes one OP or one S7 partner. When in doubt, consolidate clients behind a single OPC server to use only one slot.
What is the difference between libnodave and Snap7?
libnodave is the older open-source C library; it works reliably on S7-300/400 over ISO-on-TCP. Snap7 is a modern, actively maintained client/server library with a cleaner .NET binding, larger PDU default, and clearer error codes. For S7-1200/1500 firmware V4+ projects, Snap7 is the path of least resistance.
Why does my OPC client throw 0x80070005 against my OPC server?
That is Windows access-denied for DCOM. Open Component Services > Computers > My Computer > DCOM Config > your server. On the Security tab grant the OPC client user/group Remote Launch and Remote Activation. Also raise the default authentication to Connect on both ends. Adding the OPC service account to the local Distributed COM Users group typically resolves the error.
Can AdvancedHMI replace WinCC when communicating with Siemens PLCs?
AdvancedHMI can deliver comparable graphic and tag-binding function, but it does not ship deep integration with Siemens' alarm archive, audit trail, or recipe services. For applications that require GxP-style audit logging or TIA Portal HMI commissioning flows, keep WinCC or migrate to SIMATIC WinCC Unified. For basic visualization and operator panel applications, AdvancedHMI plus libnodave/Snap7 is a viable path.