S7-300 USB-MPI Adapter Programming with PRODAVE MPI in C#

David Krause14 min read
S7-300SiemensTechnical 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: USB-MPI Communication with S7-300

The classic route into a SIMATIC S7-300 was the RS-232 PC Adapter (MPI) running through a serial COM port. When Siemens transitioned to USB-only workstations, the parallel bridge became the 6ES7 972-0CB20-0XA0 SIMATIC PC Adapter USB, a self-powered cable that exposes an MPI/PROFIBUS DP slave on the S7 side and a USB 1.1 device on the PC side. This same adapter also ships under the later designation 6ES7 972-0CB35-0XA0 with updated RS-232/TTY firmware. Both units are electrically identical and share a common driver stack, but only one software toolbox - PRODAVE MPI/DP - exposes the synchronous MPI read/write primitives that C# applications need when SIMATIC NET is not licensed or is intentionally bypassed.

This reference covers the full path: pin and protocol layer, the hardware versions, the PRODAVE MPI/DP API surface, a working C# wrapper, the configuration of the Set PG/PC Interface tool, and a troubleshooting matrix for the field problems you will hit (baud-rate lock, MPI address clash, PLC STOP on connect, missing DLLs, .NET platform target mismatch).

Important constraint: SIMATIC NET softnet does not expose a channel for the PC Adapter USB. If you need a USB-MPI bridge into S7-300 from C# without buying S7-PLCSIM or a CP card, PRODAVE MPI/DP is the path of least resistance. Free open-source libraries (libnodave, Snap7) also work over this cable but use the FDL/PC-Adapter driver that PRODAVE installs.

Hardware: 6ES7 972-0CB20-0XA0 PC Adapter USB

The PC Adapter USB is a single-piece cable assembly, 5 m long, terminating in a 9-pin sub-D male that connects to the MPI/DP port of an S7-300 CPU (or S7-200 PPI, S7-400 MPI, C7, M7). The cable contains an RS-485 transceiver, an isolated DC/DC converter, and a microcontroller that bridges between the PC USB port and the MPI/DP bus.

Order numbers and revisions

MLFB Revision Notes
6ES7 972-0CB20-0XA0 ES 02 / FW 1.0.8 Original release. RS-232 mode only. MPI up to 1.5 Mbps.
6ES7 972-0CB35-0XA0 ES 05 / FW 2.0.x Successor. Adds TTY/20 mA mode and PROFIBUS DP up to 12 Mbps.
6GK1 571-0BA00-0AA0 CP 5711 Replacement form factor (ExpressCard/PCIe). Same protocol stack.

Electrical and protocol specifications

Parameter Value
PC interface USB 1.1 Full Speed (12 Mbps), Type-A
PLC interface RS-485 isolated, MPI / PROFIBUS DP slave
Connector 9-pin sub-D, male, pinout per EN 50170
MPI baud rate 187.5 kbps (mandatory for S7-300/400 MPI)
PROFIBUS DP max baud rate 1.5 Mbps (0CB20), 12 Mbps (0CB35)
Bus termination Internal, switchable via DIP switch on housing
Power consumption 200 mA max from USB, no external PSU required
Galvanic isolation 500 V between USB and MPI bus
Operating temperature 0 °C to 60 °C
Driver stack Siemens USB-MPI-Driver, installs virtual COM port (VCP) + S7ONLINE access point

LED and DIP switch behavior

Two status LEDs sit under the DB-9 shell: green indicates USB enumeration and PC communication, yellow indicates active token on the MPI bus. The 4-position DIP switch on the housing controls:

  1. SW1: Term - insert 220 Ω termination (only when the cable is the last segment).
  2. SW2: Mode - RS-232/TTY select on 0CB35, ignored on 0CB20.
  3. SW3: Baud - forces 19.2 kbps (PPI legacy); leave OFF for MPI 187.5 kbps.
  4. SW4: Test - reserved, must be OFF.

Software Stack: Why PRODAVE MPI/DP

The PC Adapter USB presents itself to Windows as two devices: a virtual COM port (used only for legacy S7-200 PPI passthrough) and a Siemens proprietary S7ONLINE access point. SIMATIC NET installs the S7ONLINE access point and can route OPC, S7 functions, and TCP/IP over it - but only when a SIMATIC NET license (Softnet-S7 Lean or higher) is present. Without a license, SIMATIC NET will not enumerate the cable.

PRODAVE MPI/DP is a separate toolbox, sold under MLFB 6ES7 964-2AA00-0AB0 (V6.2) or available in older V5.x variants. It installs its own MPI driver (typically named SIMATIC_MPI.DLL and W95S7API.DLL under %ProgramFiles%\Siemens\PRODAVE\MPI\) and exposes ~50 C-style functions through a single dynamic library: PRODAVE_MPI.DLL. C# callers use DllImport (P/Invoke) to bind to these functions; no separate COM interop is needed.

PRODAVE MPI/DP installed components

File Location Purpose
PRODAVE_MPI.DLL System32 (or app bin) Core API surface
W95S7API.DLL PRODAVE install folder Internal driver shim to S7API
S7API.DLL Windows\SysWOW64 Kernel-level MPI frame handler
S7ONLINE access point Registry: HKLM\SOFTWARE\Siemens\SINEC Logical device the API binds to
Set PG-PC.exe PRODAVE\Tools Selects MPI bus and bus profile

Set PG/PC Interface configuration

Before any C# code can connect, the S7ONLINE access point must be pointed at the PC Adapter. Open Set PG-PC Interface, pick PC Adapter (MPI) -> S7ONLINE, then click Properties:

  • MPI address of PC: 0 (default, address 0 is reserved for the master/PG).
  • Highest MPI address: 15 (or 31, depending on the bus scope).
  • Transmission rate: 187.5 kbps.
  • Timeout: 30 s.

On the S7-300 side, the CPU must have a free MPI slot (CPU 312-318 typically allow addresses 2..15 in addition to their default). Use STEP 7 -> PLC -> Properties -> MPI/DP Interface to confirm the address and baud.

PRODAVE MPI/DP API Reference

PRODAVE MPI/DP exposes three logical layers: connection, data-block, and bit-level. Functions return a 16-bit signed status code where 0 means OK and any other value maps to an MPI error class.

Connection management

Function Prototype Returns
Load short Load(void) 0 on success, -1 if DLL not loadable
Unload short Unload(void) 0 on success
SetActiveConnection short SetActiveConnection(short id) Connection handle
ActivateConnection short ActivateConnection(int con) 0 on connect
DeactivateConnection short DeactivateConnection(int con) 0 on disconnect
GetConnectionState short GetConnectionState(int con) 0 = idle, 1 = active

Data block read/write (byte / word / dword)

Function Operates on Length
db_read DB (any number) 1..2048 bytes
db_write DB 1..2048 bytes
mb_read Merker (M), Input (PE), Output (PA) 1..2048 bytes
mb_write Merker, Output 1..2048 bytes
z_read / z_write Counter (C) 2 bytes
t_read / t_write Timer (T) 2 bytes

Bit-level access

Bit access functions (db_bit_read, db_bit_write, mb_bit_read, mb_bit_write) take a byte offset and a bit number 0..7. These calls are slow (each one is a full MPI transaction); prefer db_read of the whole word followed by in-memory masking when polling fast bits.

Status codes

Code Meaning
0 OK
0x0001 Connection not active
0x0002 Invalid connection handle
0x0003 PLC timeout (bus dead)
0x0005 Address area out of range
0x0010 PLC object does not exist (DB not loaded, etc.)
0x0030 Wrong DB length in CPU
0x0040 PLC is in STOP / protection
0x0080 Parity / framing - check bus termination
0xFFFF Driver not loaded - PRODAVE install broken

C# Wrapper: PRODAVE_MPI.DLL via P/Invoke

PRODAVE MPI/DP is a C ABI library, so the entire surface maps cleanly onto [DllImport]. The following wrapper compiles against .NET Framework 4.8 or .NET 6 (with Windows TFM) and assumes the S7ONLINE access point is already configured to point at the PC Adapter USB.

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace S7UsbMpi
{
    public sealed class ProdaveMpi : IDisposable
    {
        // -- connection management ----------------------------------------
        [DllImport("prodave_mpi.dll", CharSet = CharSet.Auto)]
        private static extern short Load();

        [DllImport("prodave_mpi.dll")]
        private static extern short Unload();

        [DllImport("prodave_mpi.dll")]
        private static extern short SetActiveConnection(short id);

        [DllImport("prodave_mpi.dll")]
        private static extern short ActivateConnection(int con);

        [DllImport("prodave_mpi.dll")]
        private static extern short DeactivateConnection(int con);

        // -- data block I/O ----------------------------------------------
        [DllImport("prodave_mpi.dll", EntryPoint = "db_read")]
        private static extern short db_read(
            int con, short dbNo, short startByte,
            short length, byte[] buffer);

        [DllImport("prodave_mpi.dll", EntryPoint = "db_write")]
        private static extern short db_write(
            int con, short dbNo, short startByte,
            short length, byte[] buffer);

        // -- merker / IO area I/O -----------------------------------------
        [DllImport("prodave_mpi.dll", EntryPoint = "mb_read")]
        private static extern short mb_read(
            int con, short area, short startByte,
            short length, byte[] buffer);

        [DllImport("prodave_mpi.dll", EntryPoint = "mb_write")]
        private static extern short mb_write(
            int con, short area, short startByte,
            short length, byte[] buffer);

        // -- PLC state ----------------------------------------------------
        [DllImport("prodave_mpi.dll", EntryPoint = "plc_status")]
        private static extern short plc_status(
            int con, ref short runStop, ref short fault);

        // Area constants for mb_read / mb_write
        public const short AREA_PE = 0x81; // process inputs (E / PE)
        public const short AREA_PA = 0x82; // process outputs (A / PA)
        public const short AREA_MK = 0x83; // merkers (M)

        private int _conn;
        private bool _loaded;

        public ProdaveMpi(short mpiAddress = 2, int timeoutMs = 30000)
        {
            short s = Load();
            if (s != 0) throw new InvalidOperationException(
                $"PRODAVE Load failed: 0x{s:X4}. Install PRODAVE MPI/DP and verify S7ONLINE access point.");
            _loaded = true;

            // Create connection 1 -> target MPI address
            short set = SetActiveConnection(1);
            short act = ActivateConnection(mpiAddress);
            if (set != 0 || act != 0)
                throw new InvalidOperationException(
                    $"Cannot activate MPI connection. set=0x{set:X4} act=0x{act:X4}");
            _conn = mpiAddress;
        }

        public byte[] ReadDb(short dbNumber, int startByte, int length)
        {
            var buf = new byte[length];
            short s = db_read(_conn, dbNumber, (short)startByte, (short)length, buf);
            if (s != 0) throw new InvalidOperationException(
                $"db_read failed: 0x{s:X4} DB{dbNumber}.{startByte} len={length}");
            return buf;
        }

        public void WriteDb(short dbNumber, int startByte, byte[] data)
        {
            short s = db_write(_conn, dbNumber, (short)startByte,
                               (short)data.Length, data);
            if (s != 0) throw new InvalidOperationException(
                $"db_write failed: 0x{s:X4}");
        }

        public byte[] ReadMerker(int startByte, int length)
        {
            var buf = new byte[length];
            short s = mb_read(_conn, AREA_MK, (short)startByte,
                              (short)length, buf);
            if (s != 0) throw new InvalidOperationException(
                $"mb_read(M) failed: 0x{s:X4}");
            return buf;
        }

        public PlcState QueryState()
        {
            short run = 0, fault = 0;
            short s = plc_status(_conn, ref run, ref fault);
            if (s != 0) throw new InvalidOperationException(
                $"plc_status failed: 0x{s:X4}");
            return new PlcState { Running = run != 0, Faulted = fault != 0 };
        }

        public void Dispose()
        {
            if (_conn != 0)
            {
                DeactivateConnection(_conn);
                _conn = 0;
            }
            if (_loaded)
            {
                Unload();
                _loaded = false;
            }
        }
    }

    public struct PlcState
    {
        public bool Running;
        public bool Faulted;
    }
}

Usage example

using (var mpi = new ProdaveMpi(mpiAddress: 2))
{
    var state = mpi.QueryState();
    Console.WriteLine($"PLC running={state.Running} faulted={state.Faulted}");

    // Read first 20 bytes of DB10
    byte[] db10 = mpi.ReadDb(dbNumber: 10, startByte: 0, length: 20);

    // Write a counter preset into DB100.DBD0 (real)
    byte[] payload = BitConverter.GetBytes(42.0f);
    mpi.WriteDb(dbNumber: 100, startByte: 0, data: payload);
}
Project platform target: PRODAVE_MPI.DLL is x86-only. Set the C# project to AnyCPU / Prefer 32-bit on .NET Framework, or <PlatformTarget>x86</PlatformTarget> on .NET 5+. A 64-bit host process will fail Load() with 0xFFFF.

Alternatives to PRODAVE MPI/DP

PRODAVE is paid commercial software and is being phased out in favor of TIA Portal and the libnodave / Snap7 open-source ecosystem. The PC Adapter USB works with both because they call into the same Windows driver installed by the PRODAVE / SIMATIC NET setup.

libnodave (open source, C)

libnodave talks MPI/PROFIBUS over the S7ONLINE access point or directly via the CP card. Use the IBHNet or libnodave-java binding from C# via a thin wrapper. Set dave.SetS7OnlinePoint('S7ONLINE') and dave.SetInterface(IFType.MPI, 0, 187.5). libnodave is GPL but can be statically linked into commercial code with a written exception from the author Thomas Hergenhahn.

Snap7 (open source, C#/.NET)

Snap7 targets Ethernet (S7-1200/1500 + CP343) and does not natively drive the PC Adapter USB. For S7-300 over USB-MPI it is therefore not a substitute for PRODAVE. If your S7-300 has a CP343-1 Lean installed, adding the CP and moving to Snap7 is the long-term-clean solution.

S7.NET (open source, .NET)

Same caveat as Snap7: works over Ethernet/IP only. Useful as a higher-level facade if you also need symbolic tag access, but for raw DB/M/E/A via USB-MPI you must drop back to PRODAVE or libnodave.

Step-by-Step Setup Procedure

  1. Install STEP 7 V5.5 (or STEP 7 V5.6) on the target PC. This installs the S7ONLINE access point skeleton.
  2. Plug in the 6ES7 972-0CB20-0XA0. Windows should auto-detect it and install the generic USB CDC device; if not, run the manual driver located on the PRODAVE CD under Drivers\PC_Adapter_USB\ or use the Siemens AUTOMATION_INSTALLER package.
  3. Verify the cable shows up in Device Manager under Siemens AG - SIMATIC PC Adapter USB. The COM-port entry (e.g. COM5) is only used by S7-200 PPI tools; C# code via PRODAVE does not need it.
  4. Install PRODAVE MPI/DP V6.2 (MLFB 6ES7 964-2AA00-0AB0). Accept the default install folder.
  5. Open Set PG/PC Interface (start menu -> SIMATIC -> STEP 7 -> Set PG-PC Interface). Set PC Adapter (MPI) -> S7ONLINE. Click Properties: address 0, max 15, 187.5 kbps.
  6. Connect the DB-9 end to the S7-300 MPI port. Ensure termination: if this is the last node on the segment, set DIP SW1 = ON; otherwise OFF.
  7. Power-cycle the S7-300 CPU. The yellow LED on the adapter should blink once as the CPU grants the token.
  8. Build the C# wrapper project, target x86, set the working directory or PATH so prodave_mpi.dll is resolvable at runtime.
  9. Run a sanity-check program that calls QueryState(). A successful response means the full stack (USB driver -> PRODAVE -> S7ONLINE -> MPI cable -> CPU) is wired up.

Verification Checklist

Before signing off, verify all of the following. Each item is a single point of failure that the wrapper cannot distinguish from a real PLC error.

# Check Expected Tool
1 Device Manager - cable present Siemens SIMATIC PC Adapter USB, no warning triangle Device Manager
2 S7ONLINE access point PC Adapter (MPI) -> S7ONLINE, 187.5 kbps Set PG/PC Interface
3 PRODAVE Load() returns 0 C# smoke test
4 plc_status() returns 0, RUN flag = 1 C# smoke test
5 db_read of DB10.0 / length 1 returns 0, buffer populated C# smoke test
6 Yellow LED on adapter flickers during every call, then idle Visual
7 STEP 7 accessible to the bus Online -> Accessible Nodes lists the CPU STEP 7 PG mode
8 MPI address uniqueness PC = 0, CPU = 2, no other PG on bus with 0 STEP 7 HW config

Troubleshooting Matrix

Symptom Most likely cause Fix
Load() returns 0xFFFF PRODAVE not installed or 32/64-bit mismatch Install PRODAVE V6.2, set PlatformTarget = x86
ActivateConnection returns 0x0003 PLC timeout - bus dead or wrong address Confirm MPI address in STEP 7, replace cable, check 24 V on CPU
ActivateConnection returns 0x0004 Baud rate mismatch Force 187.5 kbps in Set PG/PC; verify CPU has not been set to 19.2 kbps PPI
db_read returns 0x0010 DB does not exist in CPU at runtime STEP 7 -> Blocks -> Download to CPU; check DB number matches HW config
db_read returns 0x0040 CPU in STOP or password-protected Use a PG to clear the password or restart in RUN
db_read returns 0x0030 Requested length exceeds the DB length in CPU Reduce length or extend the DB
Yellow LED never lights Bus termination or reversed polarity Set DIP SW1, swap DB-9 orientation, test on a known-good CPU
Intermittent 0x0008 / 0x0080 errors EMI on long cable, missing shield ground Add ferrite, ground DB-9 shield at PLC end only, shorten run
Only one PC can connect, second hangs Two PGs with MPI address 0 Reassign one PG to address 1 or higher in Set PG/PC
Works in STEP 7 but not in C# Set PG/PC not pointing to S7ONLINE Re-run Set PG/PC and pick PC Adapter (MPI) -> S7ONLINE
First call works, second fails Connection not reactivated after Unload Always ActivateConnection after Load(), DeactivateConnection before Unload
Bit read returns 0 but DB is non-zero Byte/bit order reversed (Big-Endian on PLC) Swap byte array before indexing bits; S7-300 stores bits in little-endian byte order
Performance < 5 read/s Polling each byte individually Batch into a single db_read of the largest required region

Safety and Operational Limits

  • The PC Adapter USB is rated for office/industrial environments, not for permanent installation in cabinets with high EMI from VFDs or welding inverters.
  • Do not plug or unplug the DB-9 connector while the CPU is in RUN; the resulting bus glitch can drop other stations (drives, ET200S) into fault.
  • Do not stack two MPI masters (PG + PRODAVE) on the same physical segment without verifying their bus addresses; collisions trigger SF on the CPU.
  • The cable is hot-pluggable on the USB side only. Hot-unplug from the MPI side is forbidden by EN 50170.

Performance Notes

At 187.5 kbps with a 64-byte DB read, the round-trip latency through PRODAVE is 25-40 ms on a modern Windows 10/11 PC. The bottleneck is the half-duplex RS-485 turnaround, not the USB pipe. For polling loops, prefer a single db_read that fetches the entire block of interest, then parse in memory. Avoid sub-byte calls - each db_bit_read is a full token-pass cycle.

Reference Documents

FAQ

Can I use the 6ES7 972-0CB20-0XA0 with SIMATIC NET instead of PRODAVE?

No. SIMATIC NET softnet does not provide a channel for the PC Adapter USB. PRODAVE MPI/DP, libnodave over the S7ONLINE access point, or STEP 7 PG mode are the only supported paths.

My C# program throws "Load failed: 0xFFFF" on Windows 11 x64 - what is wrong?

PRODAVE_MPI.DLL is 32-bit. Set your project's PlatformTarget to x86 (AnyCPU with Prefer 32-bit on .NET Framework). A 64-bit host process cannot load the DLL even when the file is present on disk.

What baud rate do I need for S7-300 MPI?

Always 187.5 kbps. The CPU does not negotiate; the PG (or PRODAVE) must match. Setting 19.2 kbps forces PPI legacy mode and the S7-300 will reject the token.

How fast can I poll a 64-byte DB?

About 25-40 ms round-trip on a typical office PC at 187.5 kbps. Avoid bit-level calls - each one is a full token-pass cycle and is roughly 10x slower.

Does the PC Adapter USB work with S7-1200 and S7-1500?

No. The 6ES7 972-0CB20-0XA0 only speaks MPI/PROFIBUS. The S7-1200/1500 family uses PROFINET only and requires a CP card, IE-capable cable, or the native Ethernet interface.

Back to blog