Visual C# .NET to S7-300 Communication: OPC vs Prodave Setup

David Krause17 min read
S7-300SiemensTutorial / How-to
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

Visual C# .NET to Siemens S7-300 Communication: OPC, Prodave, and Open-Source Paths

When porting an HMI or supervisory application from a Beckhoff TwinCAT environment to a Siemens S7-300 controller, the first engineering question is almost always the same: "How do I read and write PLC tags from a Visual C# .NET program over Industrial Ethernet?" Siemens offers two first-party toolkits (SIMATIC NET OPC Server and the Prodave/Comfort libraries) and the open-source community has produced a third path (Snap7, libnodave, S7.NetPlus) that often beats the licensed options on both cost and ease of deployment. This reference walks through every layer of the stack, the trade-offs of each option, and a working C# implementation path for both the licensed and open-source routes.

1. Problem Definition: Porting from TwinCAT ADS to SIMATIC S7

Beckhoff TwinCAT exposes the ADS (Automation Device Specification) protocol as a pure .NET class library (TcAdsDll, TcAdsApi). The application calls AdsClient.ReadAny, WriteAny, and AddDeviceNotification directly, with the TwinCAT router acting as a TCP broker on the engineering or target IPC. The mental model is "PLC = object, tag = property".

On a SIMATIC S7-300 the mental model is different. There is no in-process router and no first-party .NET class library for arbitrary tag access. Every PC-side path terminates on either:

  • A Siemens-supplied server (SIMATIC NET, Prodave, WinCC) that exposes data through a Microsoft-defined interface, or
  • A third-party or open-source implementation of the S7 communication protocol stack over ISO-on-TCP (port 102) or TCP (port 102 with TPKT).

The target functionality is identical: read a tag, write a tag, and ideally register for value changes. Only the transport and the abstraction layer change.

2. S7-300 Communication Stack: What Is Actually on the Wire

An S7-300 CPU with a PROFINET interface (CPU 31x-2 PN/DP) or a separate CP 343-1 Lean/Advanced provides the following external interfaces:

Protocol Transport Port Use Case S7-300 Support
S7 Communication (S7Comm / Put/Get) ISO-on-TCP (RFC 1006) 102 PG/HMI tag access Yes (firmware ≥ V2.x)
Open User Communication (OUC / T-blocks) TCP / UDP / ISO configurable Free-form partner messaging Yes (CP 343-1 / CPU PN)
PROFINET IO Real-time Ethernet n/a Distributed I/O CPU 31x-2 PN only
MPI / PROFIBUS-DP RS-485 / PROFIBUS n/a Legacy HMI All S7-300 CPUs
Web Server (S7-300 firmware ≥ V3.2 on PN CPUs) HTTP 80 Diagnostics pages only, not tag access Limited

For the C# → S7-300 link the relevant layer is the S7 Communication protocol (commonly called S7Comm or the Put/Get service). It runs on top of ISO-on-TCP (RFC 1006), uses TPKT/COTP framing, and exchanges user data through PDU types 0x32 (read), 0x05 (write), 0x32 with subfunction 0x04 (read SZL), and 0x00 (CPU services). The CPU must allow external Put/Get access — by default on a fresh S7-300 this is enabled, but a locked configuration in STEP 7 will block it silently with an 0x8104 error.

Engineering note: An S7-300 CPU does not initiate tag-change pushes to a PC. There is no native publisher/callback model equivalent to TwinCAT's AddDeviceNotification. Any "change notification" implementation must be polled by the PC side, or implemented on the S7-400 with WinCC/PCS7 using the Symbolic-Triggered connection, which is a CPU feature (not a software feature) and is not available on S7-300. See SIMATIC S7-300 CPU 31xC and CPU 31x: Technical specifications for the per-CPU service list.

3. Option A — SIMATIC NET S7 OPC Server (the recommended path)

The most flexible long-term answer is the SIMATIC NET S7 OPC Server bundled with the SIMATIC NET PC software DVD. The PC-side topology is:

C# Application (.NET OPC Client) SIMATIC NET S7 OPC Server (DA 3.0) CP 343-1 ISO-on-TCP /102 S7-300 CPU 31x-2 PN OPC DA / UA S7Comm PN No in-process .NET class library — the OPC client talks to an out-of-process COM/DCOM server.

3.1 Why OPC wins for multi-vendor fleets

If the same C# application is later expected to read tags from an Allen-Bradley ControlLogix, a Schneider Modicon M340, or a GE RX3i, only the OPC server (or the equivalent DA Server) needs to change. The C# client code stays identical because OPC Data Access 3.0 is vendor-neutral. This is the central reason the field report converged on OPC as the long-term answer.

3.2 License variants

SIMATIC NET variant Order number (MLFB) Max S7 connections OPC DA OPC UA Typical use
SOFTNET-IE S7 Lean 6GK1704-1LW64-3AA0 8 Yes No HMI / SCADA, single CPU
SOFTNET-IE S7 6GK1704-1LE64-3AA0 64 Yes No Multi-CPU HMI
SOFTNET-IE S7 V15+ (OPC UA) 6GK1704-1LE70-3AA0 64 Yes Yes Modern firewalls, DCOM-free
CP 1616 / CP 1626 hardnet 6GK1616-1AT01 / 6GK1626-1AA00 ≥ 120 Yes Yes High-availability / deterministic

For a single S7-300 the SOFTNET-IE S7 Lean license is the correct minimum order. A working C# example against this server is published at the Siemens support entry SIMATIC NET: Programming an OPC DA Client with C# (.NET).

4. Option B — Prodave (in-process S7 library)

Prodave (current name: SIMATIC S7-PLCSIM / Prodave V6, historically distributed as Prodave MPI/IE) is a Windows DLL that exposes the S7Comm protocol directly to user code without a server. Variants:

Prodave variant Transport Operating system .NET callable
Prodave MPI/IE V5.6 MPI, IE Windows XP/7 32-bit Yes (P/Invoke)
Prodave V6.0 (Win 7/10) IE (ISO-on-TCP), MPI optional Windows 7 32-bit only Yes (P/Invoke, 32-bit host required)
LibProdave 6.2 (.NET wrapper) IE Windows 7/10/11 32 & 64 Yes (managed, no P/Invoke)

Prodave is officially callable from C/C++ and VB. A .NET caller must declare the functions with [DllImport], or use the third-party LibProdave 6.2 managed wrapper. There is no Microsoft-supported direct C# binding. For a Visual C# project this is a non-trivial constraint: the application must either be compiled as x86 to host the 32-bit Prodave DLL, or the managed wrapper must be sourced separately.

4.1 Prodave vs OPC decision matrix

Criterion SIMATIC NET OPC Prodave / LibProdave
Deployment surface 1 server + 1 client 1 DLL in-process
DCOM configuration Required for OPC DA None
Multi-vendor support Yes (swap server) No (Siemens only)
.NET integration OPC automation wrappers, OPC .NET API P/Invoke or 3rd-party wrapper
Cost Per S7 connection Per PC license
Support contract Siemens, global End-of-life, partner-dependent
Recommendation from the field: if the C# application will only ever talk to SIMATIC controllers and the target PC is dedicated, Prodave's in-process model is faster to commission. If the application must grow to ControlLogix, Modicon, or any non-Siemens hardware, OPC keeps the client code stable and reduces the per-vendor re-write to swapping the DA server (Kepware, Matrikon, Siemens).

5. Option C — Open-Source / Third-Party Native Libraries

For engineers who do not want a licensed server and do not want DCOM, the S7-300 S7Comm protocol has been reverse-engineered to a high degree of fidelity. The three notable stacks are:

Library Language License .NET wrapper S7-300 tested
Snap7 (github.com/davenardella/snap7) C / C++ LGPL-3 Sharp7 Yes (CPU 312-319)
libnodave (Thomas Hergenhahn) C GPL-2 Manual P/Invoke Yes (older)
S7.NetPlus (github.com/S7NetPlus/s7netplus) C# MIT Native Yes

Snap7 plus the managed Sharp7 wrapper is the current industry favorite for greenfield .NET integrations. It implements the S7Comm PDU types in-process, supports parallel partner connections, and is actively maintained (current stable 1.4.x). S7.NetPlus is a pure C# implementation that wraps the same protocol and is easier to ship in a single assembly — the S7.NetPlus NuGet package installs into any .NET Framework 4.6+ or .NET 6+ project without an external native dependency.

6. Change Notification: Why S7-300 Cannot Push

The field report explicitly raised "registering for variable changes". On Beckhoff this is a first-class feature (IAdsNotification). On SIMATIC the equivalent is split across two capabilities:

  1. S7-300 cyclic service — the PC (or server) is the polling master; the CPU returns the current value on each request. There is no CPU-initiated push.
  2. S7-400 "trigger by symbol" — only available on S7-400 CPUs (e.g., 416-3, 417-4) paired with WinCC or PCS7, and only for DB tags. The CPU detects a defined value change and pushes a single telegram; the WinCC station receives it without polling.

There is no analogue on the S7-300. A C# → S7-300 change-notification feature must be emulated on the PC side with a polling loop (typically 100–500 ms, fast enough to look like an event but slow enough to avoid overwhelming the S7Comm queue). The recommended pattern is:

  1. Open a single S7.NetPlus or Snap7 connection to the CPU (rack 0, slot 2 by default for S7-300 PN).
  2. Use a System.Threading.Timer or async loop at the desired poll interval.
  3. Read the data block(s) in bulk (a single PDU can carry up to 480 bytes of contiguous data).
  4. Compare to the cached copy; only raise the C# event when the value has actually changed.
Capacity check: An S7-300 CPU 315-2 PN/DP accepts a maximum of 16 active S7Comm connections by default. Each OPC client session, each PG, and each HMI counts as one. When sizing the architecture add the SIMATIC NET server connection and any WinCC flexible runtime connection to the same budget before adding the new C# application's direct connection.

7. Multi-Vendor Strategy: Adding Allen-Bradley ControlLogix

The original requirement mentioned a follow-on project using Allen-Bradley ControlLogix. The OPC choice preserves the C# code base because:

  • Siemens path: SIMATIC NET S7 OPC server (DA 3.0) reads S7-300 DBs.
  • Rockwell path: RSLinx Enterprise (now FactoryTalk Linx) exposes ControlLogix tags through the same OPC DA 3.0 interface, or through OPC UA.
  • Universal path: Kepware KEPServerEX or Softing's OPC Router exposes both vendors in one server.

The C# client code is identical in all three cases because OPC DA 3.0 server enumeration (IOPCServerList) and item access (IOPCItemMgt, IOPCSyncIO) are vendor-neutral COM interfaces, and the .NET OPC Foundation wrapper (OPCAutomation or the newer OPC .NET Standard) consumes them uniformly.

8. Step-by-Step: Building a C# OPC DA Client for S7-300

Prerequisites:

  • SIMATIC NET PC software 2022 or newer installed (v15.1, v16, v17, v18, v19 supported). The S7 OPC server is registered as a local COM service under Siemens.SimaticNET.OPCServer.DA.
  • STEP 7 V5.7 (for S7-300) or TIA Portal V15+ (for newer projects) project with the CPU's PROFINET IP address, rack 0, slot 2.
  • Visual Studio 2019/2022 with a C# project targeting .NET Framework 4.8 (OPC DA via COM interop) or .NET 6+ with OPC UA.

8.1 Add the OPC reference

  1. In Visual Studio choose Add Reference → COM → OPC Automation 2.0 (this is the wrapper provided by the OPC Foundation; SIMATIC NET installs it).
  2. If your project targets .NET 6 or later you cannot consume the classic COM wrapper directly. Use the OPC .NET Standard package (1.5.x) plus a UA wrapper on the server side, or fall back to the .NET Framework 4.8 project type for OPC DA 3.0.

8.2 Connect to the S7 OPC server

using OPCAutomation;
using System;

namespace S7OpcClientDemo
{
    internal class Program
    {
        static void Main()
        {
            var opcServer = new OPCServer();
            // Siemens ProgID as registered by SIMATIC NET
            string progId = "Siemens.SimaticNET.OPCServer.DA";
            string node = Environment.MachineName; // local OPC server
            opcServer.Connect(progId, node);

            Console.WriteLine("Server state: " + opcServer.ServerState);
            opcServer.Disconnect();
        }
    }
}

8.3 Browse and add items

Items are addressed by the Siemens item ID convention: S7:[CPU_ip]DB<DBnum>,<byte_offset><type_letter><bit>. Example: S7:[192.168.0.10]DB1,REAL0 reads a 32-bit REAL at offset 0 of DB1.

OPCGroups groups = opcServer.OPCGroups;
OPCGroup group = groups.Add("Group1");
group.IsActive   = true;
group.IsSubscribed = true;
group.UpdateRate = 250; // ms

OPCItems items = group.OPCItems;
int clientHandle = 1;
object itemIds = new object[] { "S7:[192.168.0.10]DB1,REAL0",
                                "S7:[192.168.0.10]DB1,REAL4",
                                "S7:[192.168.0.10]DB10,BYTE0" };
int[] serverHandles = new int[3];
int[] errors        = new int[3];
items.Add((int)itemIds.Length,
          ref itemIds,
          ref clientHandle,
          out serverHandles[0],
          out errors[0]);

8.4 Read, write, and async data change

// Synchronous read
object values; object qualities; object timestamps;
items.Read((short)serverHandles.Length,
           serverHandles,
           out values,
           out qualities,
           out timestamps,
           out errors);

// Asynchronous subscription (closest analogue to TwinCAT AddDeviceNotification)
group.DataChange += OnGroupDataChange;

private static void OnGroupDataChange(int transactionId, int numItems, ref Array clientHandles,
                                      ref Array itemValues, ref Array qualities, ref Array timestamps)
{
    for (int i = 1; i <= numItems; i++)
    {
        Console.WriteLine($"handle={clientHandles.GetValue(i)} val={itemValues.GetValue(i)} ts={timestamps.GetValue(i)}");
    }
}
DCOM pitfall: if the OPC server and the C# client are on different Windows machines you must configure DCOM security, firewall rules for OpcEnum (port 135 + dynamic RPC), and run both sides under matching user accounts. Modern projects should pick the OPC UA variant of SIMATIC NET to skip DCOM entirely. See SIMATIC NET: OPC UA configuration in the S7 OPC UA server.

9. Step-by-Step: Using S7.NetPlus (open-source) from C#

This is the path that avoids any Siemens license when the project is single-vendor and the same C# code is the only consumer.

9.1 Install the package

dotnet add package S7NetPlus --version 2.2.0

9.2 Connect and read

using S7.Net;
using S7.Net.Types;

using var plc = new Plc(CpuType.S7300,
                        "192.168.0.10",
                        0,      // rack
                        2);     // slot (CPU 31x-2 PN default)
plc.Open();

if (plc.IsConnected)
{
    float  temperature = plc.Read("DB1.DBD0");     // REAL at DB1 byte 0
    bool   valveOpen   = plc.Read("DB1.DBX10.0");
    ushort word        = plc.Read("DB10.DBW2");
    Console.WriteLine($"{temperature} {valveOpen} {word}");
}

9.3 Write a value

plc.Write("DB1.DBD0",  23.5f);     // REAL
plc.Write("DB1.DBX10.0", true);   // BOOL bit
plc.Write("DB10.DBW2", (ushort)42);

9.4 Emulated change notification (polling)

var lastSeen = new Dictionary<string, object>();
var timer = new System.Threading.Timer(_ => {
    float t = plc.Read("DB1.DBD0");
    if (!lastSeen.TryGetValue("temp", out var prev) || !prev.Equals(t))
    {
        lastSeen["temp"] = t;
        OnTemperatureChanged?.Invoke(t);
    }
}, null, 0, 250);

10. S7-300 Hardware Sizing for PC Communication

When the S7-300 is in the cabinet and a new C# application will be added, verify the following before commissioning:

Item Check Acceptance
Connection resources PG/OP connections configured in STEP 7 At least 1 free for the C# client (max 16 on CPU 315-2 PN)
Put/Get permission CPU properties → Protection → Permit access with PUT/GET Enabled (default on, but can be locked)
CP firmware CP 343-1 firmware ≥ V2.0 Supports S7 communication
DB accessibility DB optimized block attribute Must be non-optimized (standard access) for S7Comm byte-offset addressing
Cycle / OB1 Max cycle time vs. requested poll rate Poll rate ≥ 2× OB1 cycle to avoid blocking the PG queue
Network CP 343-1 IP, subnet, gateway Reachable from the PC, no managed switch storm control
DB optimization gotcha: In TIA Portal V13+ any newly created DB defaults to "Optimized block access". S7.NetPlus and Snap7 read by absolute byte offset, which fails or returns garbage on an optimized DB. Either uncheck the optimized flag in the DB properties, or expose the tags symbolically and use the symbolic addressing mode that S7.NetPlus and Snap7 support in newer firmware.

11. Performance, Timing, and Error Codes

11.1 Typical round-trip times

Path Typical read latency (single tag, 100 Mbps LAN) CPU load impact
SIMATIC NET OPC — single-item read 15–40 ms Low (1 PDU per request)
SIMATIC NET OPC — subscribed group (10 items, 250 ms) ~250 ms effective Low (10 PDUs / 250 ms)
Prodave single read 3–8 ms Low
Snap7 / Sharp7 single read 3–10 ms Low
S7.NetPlus single read 4–12 ms Low
Bulk PDU read of full DB (240 bytes) 6–15 ms Lowest per-byte cost

11.2 Common S7Comm error codes

Error byte Meaning Likely cause
0x8104 No connection / wrong slot CPU not reachable on IP/port, or rack/slot mismatch
0x8105 Unknown PDU Old CPU firmware; update CPU or CP
0x8304 Read denied — object does not exist DB number wrong, or DB optimized-block attribute set
0x8402 CPU in STOP CPU is not in RUN; PLC diagnostics required
0x8404 Sequence error Re-establish connection
0x8500 Wrong PDU size Reduce request to ≤ 480 bytes per PDU
0xD401 / 0xD402 / 0xD403 Function not allowed / invalid param / ISO-on-TCP resource Put/Get not permitted, security level wrong, or CP resources exhausted

12. Verification & Diagnostics

  1. Layer 1–2: From the PC, ping 192.168.0.10. Use arp -a to confirm MAC learning on the switch.
  2. Layer 3: From the PC open TCP/102 with Test-NetConnection 192.168.0.10 -Port 102 (PowerShell) or nc -vz 192.168.0.10 102. A successful connect proves the CP 343-1 is accepting ISO-on-TCP.
  3. STEP 7 online: Open the S7 project online, expand Accessible nodes, confirm the CPU appears. This proves that the same connection budget the C# application will use is functional from STEP 7.
  4. OPC ping: The SIMATIC NET OPC Scout tool (Start → SIMATIC → SIMATIC NET → OPC Scout) browses the server, adds a single item, and forces a read/write. If OPC Scout works and the C# client does not, the problem is in the C# client, not the network or the S7-300.
  5. End-to-end smoke test: In the C# application, read a known DB byte (e.g. DB1.DBB0 set to a constant in OB1) and assert the value matches.

13. Troubleshooting Matrix

Symptom First check Most likely cause Fix
OPC Scout shows "Quality: Bad" CPU online diagnostic buffer Connection rejected, Put/Get disabled Enable Put/Get in CPU properties, download HW config
C# client receives 0x8104 Ping + port 102 Firewall or wrong IP Open TCP/102, verify CP IP
Data read is constant 0 / always old value STEP 7 monitor on the DB Optimized DB access Uncheck "Optimized block access"
Tag values are scrambled bytes Byte order S7 is big-endian, C# is little-endian Use BitConverter.ToSingle(BitConverter.GetBytes(value), 0) or library conversion
DCOM error 0x80070005 in C# dcomcnfg Access denied across machines Configure DCOM security, use matching accounts, or switch to OPC UA
OPC server disconnects every ~30 s Keep-alive setting PC network adapter power saving Disable NIC power management
Snap7 / S7.NetPlus reports "ISO: Invalid PDU" PCAP/Wireshark Router or firewall modifies TPKT Use direct PC↔CP connection for test
CPU diagnostic buffer: "Connection broken, local ID xxxx" Connection resources More than 16 S7Comm connections requested Reduce concurrent clients or upgrade to CPU 319 PN/DP

14. Selection Flowchart

Must support non-Siemens PLCs in future? YES → SIMATIC NET OPC + RSLinx / Kepware for AB NO → Siemens-only forever? Use Prodave or LibProdave Use Snap7 / S7.NetPlus

15. Field-Engineer Notes

  • License of the OPC server is per S7 connection, not per PC. If the C# application reads 4 different S7-300 CPUs, count the OPC connection licenses, not the number of running clients.
  • Symbolic addressing on S7-300 with S7Comm works only on firmware that supports it (V3.x on 31x-2 PN, V2.x on CP 343-1 V2.x). On older firmware the address must be absolute (DB number + byte offset).
  • DCOM is the #1 reason OPC projects fail late. If you can choose OPC UA from the start, choose it. The SIMATIC NET variants supporting OPC UA are the V15+ SOFTNET-IE S7 and the newer SIMATIC NET 2022 editions.
  • Snap7 + Sharp7 is the path of least resistance for a single-vendor, license-free .NET deployment and is the de-facto standard for small SCADA / data-acquisition projects talking to S7-300 / S7-400 / S7-1200 / S7-1500.
  • Do not attempt to emulate AddDeviceNotification with sub-second polls. S7-300 OB1 cycle plus the S7Comm queue cannot absorb < 50 ms polling from multiple clients. Stay at 200–500 ms per subscription group.

What is the minimum license to read S7-300 tags from a C# OPC client?

For a single CPU, order SIMATIC NET SOFTNET-IE S7 Lean (MLFB 6GK1704-1LW64-3AA0). It exposes the Siemens.SimaticNET.OPCServer.DA ProgID and supports up to 8 S7 connections. For a multi-vendor fleet, the V15+ SOFTNET-IE S7 with OPC UA (6GK1704-1LE70-3AA0) is the modern choice and removes DCOM.

Can an S7-300 push tag changes to the PC the way TwinCAT does?

No. Tag-change subscription is a CPU feature of the S7-400 family running WinCC/PCS7 only. On S7-300 the PC must poll, typically at 200–500 ms, and raise the C# event only when the polled value differs from the cached one. This emulates change notification at the application layer.

Why does my C# client read constant 0 from a DB that clearly has data in STEP 7?

Most often the DB has the "Optimized block access" attribute set in TIA Portal, which hides the byte layout. S7Comm (S7.NetPlus, Snap7, OPC) addresses by absolute byte offset, so it cannot find the data. Uncheck the optimized flag in the DB properties, recompile, and download the blocks to the CPU.

Prodave or OPC — which is faster to commission?

For a single-vendor, single-PC project, Prodave (or its managed wrapper LibProdave 6.2) is faster: it is an in-process DLL with no DCOM, no server, and one line of code to read a tag. For any project that may grow to Allen-Bradley, Modicon, or remote PC clients, OPC is the correct choice because the C# client code is vendor-neutral and survives hardware migrations.

How many S7-300 connections can my C# application use?

An S7-300 CPU 315-2 PN/DP allows 16 active S7 connections by default. Each PG, each HMI, the SIMATIC NET server (if used), and the C# application each consume one. If you exceed the budget the CPU drops new connection requests with diagnostic-buffer entry "connection resources exhausted". Switch to a CPU 317-2 PN/DP or 319-3 PN/DP for higher limits, or use a CP 343-1 with its own connection pool.

Is the open-source S7.NetPlus library production-safe?

For non-safety, non-silver-certificate HMI and data-acquisition use, yes. The library is MIT-licensed, has been used in industrial installations for over a decade, and is the same protocol stack (S7Comm over ISO-on-TCP/102) that SIMATIC NET uses. For safety-relevant control, retain the licensed SIMATIC NET path or the safety-relevant Profibus/PROFIsafe stack.

Back to blog