Problem Overview: VB.NET Reading Nothing from a Siemens S7-300 via OPC DA
Engineers building VB.NET host applications that read process data from a Siemens SIMATIC S7-300 controller through the SIMATIC NET OPC DA server frequently hit two coupled symptoms: the S7 connection diagnostic reports State = NOT_USED (or the client ServerState flag never transitions to OPCRunning), and the Values() array returned from a synchronous or asynchronous Read returns Nothing. The two problems share a root cause family: the underlying COM/DCOM channel, the S7 connection, or the OPC group is not being instantiated before the read call, or the exception is being swallowed by an over-broad Catch block that lets the code fall through into UI code that references an uninitialized array.
This reference walks through the Siemens-side configuration, the VB.NET COM interop pattern, the diagnostic steps that confirm which layer is failing, and the verification sequence that proves the data path end-to-end. It is written for control engineers who already have STEP 7 / SIMATIC Manager installed and who are targeting an S7-300 CPU (CPU 31x series) from a Windows host.
Architecture: From S7-300 Tags to a VB.NET TextBox
The data path traversed by a typical read is:
- S7-300 CPU holds a DB/M/I/Q address in its work memory.
- An S7 connection (MPI, PROFIBUS, or Industrial Ethernet CP) carries the request from the PC station to the CPU.
- The SIMATIC NET OPC DA server on the PC station translates the S7 protocol into OPC DA items.
- The VB.NET client binds to the OPC server through COM/DCOM (or locally through the
OpcEnumservice and theOPC.SimaticNetProgID). - Item handles are added to an
OPCGroupand aSyncReadorASyncReadreturns aValuesarray ofObject.
A failure at any single layer manifests as Nothing in the client. The most efficient diagnostic is to test each layer with a known-good tool, starting from the bottom:
- S7 connection: SIMATIC Manager online diagnostics, NCM S7 Diagnostic, or
pingto the CP IP address. - OPC server: OPC Scout (ships with SIMATIC NET) — confirms items resolve and the S7 connection is in the correct state.
- COM/DCOM: Windows
dcomcnfgand the OPCEnum browsing from a separate test client. - VB.NET client: minimum reproducible console application before touching the WinForms UI.
Prerequisites and Required Software Stack
| Component | Required Version / Catalog | Notes |
|---|---|---|
| S7-300 CPU | CPU 312 / 314 / 315 / 317 / 319 (Firmware ≥ V2.x for most data blocks) | Must have an active communication interface: CP 343-1 (Ethernet), CP 342-5 (PROFIBUS), or integrated MPI port. |
| STEP 7 / SIMATIC Manager | V5.5 + SP4 or V5.6 | Used to author the S7 connection in the PC station project. |
| SIMATIC NET PC Software | V14 SP1 or later (V15, V16, V17 supported) | Provides the OPC DA server (OPC.SimaticNet), OPC Scout, and the S7ONLINE access point. |
| .NET Framework | 4.6.1 or 4.8 (WinForms or WPF host) | OPC DA Automation wrapper is COM-based; .NET projects must target x86 or set the COM reference correctly. |
| OS | Windows 7 Professional SP1 (legacy), Windows 10 LTSC 2019, Windows Server 2016/2019 | DCOM defaults differ between Windows 7 and 10/Server 2016; the SIMATIC NET install must be the matching edition. |
| Visual Studio | 2017, 2019, or 2022 with VB.NET workload | Reference: OPC DA Automation 2.0 wrapper, COM tab » "OPC DA Automation 2.0". |
Siemens-Side Configuration: Building the S7 Connection
The State = NOT_USED error reported by the S7 Connection Diagnostic almost always means the PC station's S7 connection is not bound to a currently running OPC application, or the connection has been compiled but not downloaded. The connection lives inside the PC station project, not inside the S7-300 project.
- Open the PC station project in SIMATIC Manager (or TIA Portal if the PC station is V14+).
- Insert an OPC Server in slot 1 of the PC station.
- Insert the CP (e.g., IE General for the Ethernet port used) and configure its IP address, subnet, and station name. The station name must match the computer name recorded in the Windows
Station Configuration Editoron the runtime PC. - Right-click the OPC Server » New Connection and select the S7-300 station from the accessible nodes list. Choose a connection type (S7 Connection) and assign a local connection ID (typically 1).
- Right-click the S7 connection » Object Properties » Connection Mechanism and confirm the partner CPU rack/slot.
- Compile the PC station (PC » Compile and Download Objects) and download the configuration to the PC station runtime. The XDB is written into the SIMATIC NET install directory and the S7 connection is bound.
- Restart the
S7ONLINEservice (or thesimaticnetservice on newer versions) so the S7 connection transitions out ofNOT_USED.
NOT_USED. The SIMATIC NET PC Software manual lists the service restart order required after a host rename.Reference: S7 Connection State Machine
| State | Meaning | Recovery |
|---|---|---|
| NOT_USED | XDB configuration is present but no OPC application has opened the connection. | Start the OPC application that owns the S7 connection; verify Station Configuration Editor matches the host name. |
| CONNECTING | Trying to establish the S7 transport layer (TCP/MPI/DP). | Wait; if persistent, check cabling and CP IP. |
| CONNECTED | Transport up; no S7 partner connection yet. | None — intermediate state. |
| ESTABLISHED | Full S7 connection is up and ready to read. | None — proceed with item add and read. |
| DISCONNECTING / DOWN | CP link lost or CPU STOPped. | Check CPU state, physical link, and S7ONLINE logs. |
Reference VB.NET Code: Minimum Reproducible OPC DA Read
Before placing OPC code inside a WinForms button event, build a console application that performs the same sequence. This isolates the COM interop from UI threading and exception-handling issues. The wrapper used is the OPC DA Automation 2.0 type library (OPCDAAuto.dll), which is COM and must be referenced from the COM tab in Visual Studio.
' VB.NET — Console reference client. Target: x86. Requires reference to
' COM > OPC DA Automation 2.0 (OPCDAAuto.dll, shipped with SIMATIC NET)
Imports OPCAutomation
Module Module1
Sub Main()
Dim server As OPCServer = Nothing
Dim group As OPCGroup = Nothing
Try
' 1. Connect to the SIMATIC NET OPC DA server by its canonical ProgID.
server = New OPCServer()
server.Connect("OPC.SimaticNet") ' Local server. For remote, append node name.
Console.WriteLine("ServerState = " & server.ServerState.ToString())
' 1 = running, 0 = stopped, 2 = suspended, 3 = test, 4 = unknown, 5 = not connected
' 2. Create a private group with active updates disabled so that
' SyncRead is deterministic.
group = server.OPCGroups.Add("MyGroup")
group.IsActive = False
group.IsSubscribed = False
group.UpdateRate = 250
' 3. Build the item list. Read 10 words from DB1 starting at byte 0
' of a S7-300, using the S7 syntax expected by SIMATIC NET:
' S7:[<connection>]<area><length><address>
Dim clientHandles As Integer() = {1, 2, 3}
Dim itemIDs As String() = {
"S7:[S7_Connection_1]DB1,W0",
"S7:[S7_Connection_1]DB1,W2",
"S7:[S7_Connection_1]DB1,W4"
}
Dim items As OPCItems = group.OPCItems
items.AddItems(itemIDs.Length, itemIDs, clientHandles)
' 4. Synchronous read.
Dim values(2) As Object
Dim errors(2) As Integer
group.SyncRead(OPCAutomation.OPCDataSource.OPCDevice, 3, clientHandles, values, errors)
' 5. Inspect results. NEVER assume values(0) is usable if any error != 0.
For i As Integer = 0 To 2
If errors(i) <> 0 Then
Console.WriteLine("Item " & i & " error code 0x" & errors(i).ToString("X"))
ElseIf values(i) Is Nothing Then
Console.WriteLine("Item " & i & " returned Nothing")
Else
Console.WriteLine("Item " & i & " = " & Convert.ToString(values(i)))
End If
Next
Catch ex As COMException
' 0x80040154 = Class not registered (server ProgID wrong)
' 0x80070005 = Access denied (DCOM permission issue)
' 0x800706BA = RPC server unavailable (simaticnet service stopped)
Console.WriteLine("COMException 0x" & Hex(ex.ErrorCode) & ": " & ex.Message)
Finally
If group IsNot Nothing Then group.Remove(True)
If server IsNot Nothing Then server.Disconnect()
End Try
End Sub
End Module
Why Values(1) Is Nothing: The Two Root Causes
Root Cause A — The S7 connection is in state NOT_USED
If the SIMATIC NET OPC server has not bound the S7 connection to an active group, SyncRead returns a Values array whose elements are Nothing and a per-item Errors array whose values are the OPC DA quality code 0xC0040007 (OPC_QUALITY_NOT_USED) or the S7 server-specific code 0x80000000 | E_NO_VALID_S7_CONNECTION. The client should treat either code as "transport not ready" and re-try after fixing the configuration described above.
Root Cause B — The Catch block swallowed the exception
Typical WinForms pattern that produces Values = Nothing:
Try
server.Connect("OPC.SimaticNet")
group.SyncRead(OPCDataSource.OPCDevice, n, handles, values, errors)
' ...
label2.Text = values(1).ToString()
Catch ex As Exception
' broad catch — execution falls through to label2 update
End Try
If Connect throws COMException 0x800706BA (RPC server unavailable, because the simaticnet service is stopped), the Values array is never populated. The subsequent assignment label2.Text = values(1).ToString() either throws NullReferenceException or silently writes an empty string. The fix is to Return from the subroutine on any caught exception and to never assume values(i) is non-null.
Hex(ex.ErrorCode) in the catch block to record the actual HRESULT. The most common codes seen in S7-300 deployments are documented in the SIMATIC NET OPC server error code reference.Step-by-Step Diagnostic Procedure
-
Validate the S7 connection in OPC Scout. OPC Scout ships with SIMATIC NET and can resolve items from a known-good UI. Add a group, browse the DA server, and add an item such as
S7:[S7_Connection_1]DB1,W0. If Scout reads a value but your VB.NET client does not, the problem is on the client. -
Check the S7 Connection Diagnostic. From SIMATIC Manager, expand the PC station » OPC Server » right-click the S7 connection » Diagnostics. Confirm the state is
ESTABLISHED. A persistentNOT_USEDindicates a service or station-name mismatch. -
Verify the Station Configuration Editor matches the host. Open Station Configuration Editor (Start » SIMATIC » SIMATIC NET » Configuration). The component name listed under the OPC server and CP must match the PC station name compiled in STEP 7. A renamed host after cloning a VM is the most common cause of a stuck
NOT_USEDstate. -
Confirm the simaticnet / S7ONLINE service is running. Open services.msc. The service is named
simaticnet(Windows 10/Server 2016) orS7ONLINE(Windows 7). It must be Started and set to Automatic. -
Validate the .NET project platform target. OPC DA Automation 2.0 is a 32-bit COM library. Build » Configuration Manager » Platform =
x86. AnyCPU on a 64-bit OS will fail to marshal COM pointers and throwCOMException 0x80040154. - Check DCOM access permissions. On a hardened Windows 10 / Server 2016 install, Component Services » Computers » My Computer » DCOM Config » OPC.SimaticNet » Properties » Security. Add the user account running your VB.NET application to the Launch and Access Permissions with default rights.
- Run the console client above. The console has no UI thread to obscure exceptions. If it succeeds, port the same code into a WinForms application with the exception handling fixes from the next section.
Corrected WinForms Pattern
Private Sub btnRead_Click(sender As Object, e As EventArgs) Handles btnRead.Click
Dim server As OPCServer = Nothing
Dim group As OPCGroup = Nothing
Dim values(2) As Object
Dim errors(2) As Integer
' Reset the label so a failed read does not leave stale text on screen.
label2.Text = String.Empty
Try
server = New OPCServer()
server.Connect("OPC.SimaticNet")
If server.ServerState <> OPCAutomation.OPCServerState.OPCRunning Then
MessageBox.Show("OPC server state is " & server.ServerState.ToString())
Return ' <-- CRITICAL: do not fall through into the read
End If
group = server.OPCGroups.Add("MyGroup")
group.IsActive = False
group.IsSubscribed = False
group.UpdateRate = 250
Dim handles As Integer() = {1, 2, 3}
group.OPCItems.AddItems(3,
New String() {
"S7:[S7_Connection_1]DB1,W0",
"S7:[S7_Connection_1]DB1,W2",
"S7:[S7_Connection_1]DB1,W4"
}, handles)
group.SyncRead(OPCAutomation.OPCDataSource.OPCDevice, 3, handles, values, errors)
' Per-item validation: do not call .ToString() on Nothing.
If errors(0) = 0 AndAlso values(0) IsNot Nothing Then
label2.Text = Convert.ToString(values(0))
Else
label2.Text = "(no data — error 0x" & errors(0).ToString("X") & ")"
End If
Catch ex As COMException
label2.Text = "COM error 0x" & Hex(ex.ErrorCode)
Catch ex As Exception
label2.Text = "Client error: " & ex.Message
Finally
If group IsNot Nothing Then group.Remove(True)
If server IsNot Nothing Then server.Disconnect()
End Try
End Sub
Return immediately when the server state is wrong, initializes label2.Text to an empty string, and checks errors(i) and Is Nothing before calling .ToString() on the value. These three changes eliminate the original Values(1) is Nothing failure mode.Common OPC DA Quality Codes Encountered with S7-300
| Code | Meaning | Likely Cause |
|---|---|---|
| 0x00000000 | Good, value valid | None — normal operation. |
| 0xC0040007 | OPC_QUALITY_NOT_USED | S7 connection not yet established or item handle stale. Re-add the item. |
| 0x80070005 | Access denied | DCOM access permission missing for the calling user. |
| 0x800706BA | RPC server unavailable | simaticnet / S7ONLINE service stopped or firewall blocking TCP 135 + dynamic ports. |
| 0x80040154 | Class not registered | OPC server ProgID typo, or wrong platform target (AnyCPU vs x86). |
| 0x80004005 | Unspecified failure | Check S7 connection in SIMATIC Manager; often wrong rack/slot or wrong DB number. |
Item Address Syntax for S7-300
The SIMATIC NET item path follows the form S7:[<ConnectionName>]<Area><DataType><Offset>. Common variations for an S7-300 CPU:
| Address | Meaning |
|---|---|
S7:[S7_Connection_1]DB1,W0 |
Word at byte 0 in DB1. |
S7:[S7_Connection_1]DB1,DBD4 |
Double word at byte 4 in DB1. |
S7:[S7_Connection_1]DB1,REAL4 |
Real at byte 4 in DB1. |
S7:[S7_Connection_1]E0.0 |
Bit 0 of input byte 0 (I0.0). |
S7:[S7_Connection_1]A4.1 |
Bit 1 of output byte 4 (Q4.1). |
S7:[S7_Connection_1]MW10 |
Word at flag (Merkur) byte 10. |
The connection name is the name assigned in the PC station configuration (default S7_Connection_1). If the name contains spaces, surround it with single quotes inside the brackets.
Troubleshooting Matrix
| Symptom | First Check | Resolution |
|---|---|---|
ServerState = NotConnected after Connect
|
simaticnet service status, S7ONLINE access point | Start the service; rebuild XDB with Station Configuration Editor » Export Station / Import Station. |
S7 Connection Diagnostic shows NOT_USED
|
Station Configuration Editor ↔ PC station name | Rename host, restart service, or re-import the XDB. See SIMATIC NET PC Software manual. |
| values(i) is Nothing, errors(i) = 0xC0040007 | Item handle validity | Re-add items after each Group.Add; never cache item handles across reconnects. |
COMException 0x80040154 on New OPCServer()
|
Project platform target | Set to x86; rebuild. |
COMException 0x800706BA on Connect
|
Windows firewall, DCOM ports | Allow simaticnet.exe inbound on TCP 135 + dynamic RPC; see Microsoft DCOM technical reference. |
| label2.Text empty, no exception | Catch block swallowing exception | Add Return in catch; log Hex(ex.ErrorCode); do not fall through into UI update. |
| Values returned but in wrong byte order | Endianness of S7-300 | S7-300 is big-endian on the wire; BitConverter.ToInt16 on a little-endian host will swap — reverse the byte array before conversion, or use a struct with <StructLayout(LayoutKind.Explicit)>. |
Verification: End-to-End Read Confirm
- Open OPC Scout and add a group that reads
S7:[S7_Connection_1]DB1,W0. Confirm the displayed value matches the actual PLC tag. - Open the VB.NET console client from this article on the same host. Confirm
ServerState = OPCRunningand that the per-item loop prints the same value Scout shows. - Stop the CPU (simulated or real), confirm the S7 connection state in OPC Scout changes to
DOWNand the client'svalues(i)returnsNothingwith an error code documented in the matrix above. This proves the application is reporting failure rather than silently producing stale data. - Restart the CPU, wait for the S7 connection to transition back to
ESTABLISHED, and confirm the read resumes without restarting the VB.NET application — this validates the reconnect path.
Related References and Companion Reading
- SIMATIC NET OPC server error code reference — official Siemens KB.
- SIMATIC NET PC Software manual — S7 connection lifecycle and XDB handling.
- OPC DA 2.0 / 3.0 specification — OPC quality codes, SyncRead semantics.
- Microsoft DCOM technical reference — port and permission details for hardened hosts.
- Microsoft Learn — Read from text files in VB.NET — companion pattern for reading logged CSV/ASCII output from the S7-300 once OPC is stable.
Why does my S7 connection show NOT_USED even though the XDB is downloaded?
The XDB is loaded but no OPC application has opened the connection. Confirm the Windows host name matches the PC station name in Station Configuration Editor, then start the simaticnet / S7ONLINE service. The connection transitions to ESTABLISHED the first time your VB.NET application calls server.Connect("OPC.SimaticNet") and adds a group.
My values array is full of Nothing but no exception is thrown — what is wrong?
SyncRead still returns without throwing, but the per-item errors array contains the OPC quality code (0xC0040007 for NOT_USED or 0x80004005 for an unspecified S7 error). Iterate the errors array, log each code as hex, and never call .ToString() on a Nothing element. A broad Catch block that does not return is a very common secondary cause.
Do I need a 64-bit OPC server for my x64 Windows 10 host?
No. The OPC DA Automation 2.0 wrapper (OPCDAAuto.dll) is 32-bit. Build the VB.NET project as x86. If you must run as x64, migrate to OPC UA (.NET Standard library) instead of OPC DA.
Can I read multiple DBs in one SyncRead call?
Yes. Add one item per address (for example S7:[S7_Connection_1]DB1,W0 and S7:[S7_Connection_1]DB2,W0) to the same group, then call SyncRead with the full client-handle list. Mixing data types is permitted as long as each value is cast to the correct .NET type after the read.
What is the difference between S7:[S7_Connection_1]DB1,W0 and the address syntax STEP 7 uses online?
STEP 7 uses absolute symbolic offsets (DB1.DBW0); the SIMATIC NET item path is the bracket form S7:[ConnectionName]AreaDataTypeOffset. The connection name is configured in the PC station, not in the S7-300 project, and defaults to S7_Connection_1.