7.0 KiB
Siemens S7 Driver
Getting-started guide for the Siemens S7 driver. This is the short path — for
the full per-field spec read docs/v2/driver-specs.md §5,
for hands-on CLI testing read Driver.S7.Cli.md, and for
the test-harness map read S7-Test-Fixture.md.
What it talks to
Siemens S7 PLCs — S7-300, S7-400, S7-1200, S7-1500, plus S7-200 / S7-200 Smart
/ LOGO! 0BA8 — over the native S7comm protocol on ISO-on-TCP, TCP port
102. The wire is spoken by the pure-managed S7netplus
(S7.Net) library: no native DLL, no P/Invoke, no out-of-process isolation. The
driver runs in-process in the OtOpcUa server's .NET 10 AnyCPU host on every OS
the server runs on.
This is the leanest OtOpcUa driver — read/write/subscribe/discover plus a connectivity probe, and nothing else. It implements no alarm source and no per-call host resolver (a single S7 instance targets a single CPU).
Project split
| Project | Target | Role |
|---|---|---|
src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/ |
net10.0 | In-process driver — hosts the S7.Net.Plc connection and the address parser |
src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts/ |
net10.0 | Dependency-free config records + enums (S7DriverOptions, S7CpuType, S7DataType) bound from DriverConfig JSON |
Minimum deployment
Register the driver instance in the central config DB (or appsettings.json).
No separate service, no DLL deployment:
"Drivers": {
"s7-line-1": {
"Type": "S7",
"Config": {
"Host": "10.20.30.40",
"CpuType": "S71500",
"Rack": 0,
"Slot": 0,
"Tags": [
{ "Name": "Running", "Address": "DB1.DBX0.0", "DataType": "Bool", "Writable": false },
{ "Name": "Speed", "Address": "DB1.DBD4", "DataType": "Float32", "Writable": true }
]
}
}
}
S7 exposes a symbol table, but S7.Net does not surface it — so the driver
operates off a static, per-site tag list, not live symbol discovery.
Rack / slot / CPU family
CpuType selects the ISO-TSAP slot byte used during the connection handshake;
pick the family that matches the PLC exactly. Rack is almost always 0
(relevant only for distributed S7-400 racks). Slot conventions per family:
S7-300 = slot 2, S7-400 = slot 2 or 3, S7-1200 / S7-1500 = slot 0 (onboard PN).
A wrong slot causes a connection refusal during the handshake. See
src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Contracts/S7DriverOptions.cs for the
per-field defaults.
Address forms
Addresses use Siemens TIA-Portal / STEP 7 Classic syntax, parsed by
src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7AddressParser.cs:
| Area | Example | Meaning |
|---|---|---|
| Data block | DB1.DBX0.0 / DB1.DBW0 / DB1.DBD4 |
DB number + size suffix X(bit) / B(byte) / W(word) / D(dword), optional .bit for DBX |
| Merker (M) | MB0 / MW0 / MD4 / M0.0 |
Marker byte; size prefix B/W/D, or bare offset .bit for bit access |
| Input (I) | IB0 / IW0 / I0.0 |
Process-image input |
| Output (Q) | QB0 / QW0 / Q0.0 |
Process-image output |
Parsing is strict and runs once at InitializeAsync so a config typo fails fast
at load instead of surfacing as BadInternalError on every read. Bit offsets
must be 0-7, byte offsets non-negative, DB numbers >= 1.
Timer (
T{n}) and Counter (C{n}) addresses parse cleanly but the read path has no decode case for them yet — the driver rejects them at init with an explicit error rather than letting them surface a misleading type-mismatch.
Data types
S7DataType declares the semantic type; S7.Net returns an unsigned boxed
value (bool / byte / ushort / uint) that the driver reinterprets without an
extra PLC round-trip. Wired through today: Bool, Byte, Int16, UInt16,
Int32, UInt32, Float32. Int64, UInt64, Float64, String, and
DateTime are declared in the enum but rejected at init — half-implemented
types must not create OPC UA nodes that then return BadNotSupported on every
access.
Capability surface
S7Driver : IDriver, ITagDiscovery, IReadable, IWritable, ISubscribable, IHostConnectivityProbe
(src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7/S7Driver.cs).
| Capability | Path | Notes |
|---|---|---|
IReadable |
ReadAsync → S7.Net.Plc.ReadAsync |
One request/response per tag, serialized on a per-PLC semaphore |
IWritable |
WriteAsync → S7.Net.Plc.WriteAsync |
Read-only tags (Writable=false) return BadNotWritable |
ITagDiscovery |
DiscoverAsync |
Emits a flat S7/ folder of the configured tags — no live browse |
ISubscribable |
per-tag poll loop with capped exponential backoff | S7 has no push model; floor is 100 ms (the CPU services the comms mailbox once per scan) |
IHostConnectivityProbe |
periodic S7.Net.Plc.ReadStatusAsync (CPU-status PDU) |
host:port host key; Running/Stopped transitions raise OnHostStatusChanged |
Single-connection policy
One S7.Net.Plc instance per PLC, serialized with a SemaphoreSlim.
Parallelising reads against a single CPU doesn't help — the CPU scans its
comms mailbox at most once per cycle and queues concurrent requests wire-side
anyway, while wasting the CPU's 8-64 connection-resource budget.
PUT/GET communication
S7-1200 / S7-1500 ship with PUT/GET access disabled by default. A driver
pointed at a freshly-flashed CPU sees a hard access-denied fault. The driver
maps it specifically to BadNotSupported, flags the instance Faulted (a
configuration alert, not a transient fault), and does not blind-retry —
because the CPU will keep refusing. Fix: enable PUT/GET communication in TIA
Portal under Protection & Security for the CPU.
Error mapping
| Condition | StatusCode | Health |
|---|---|---|
| Tag not in config | BadNodeIdUnknown |
unchanged |
| Read-only tag written | BadNotWritable |
unchanged |
| Unimplemented data type | BadNotSupported |
unchanged |
| PUT/GET denied | BadNotSupported |
Faulted (config alert) |
| CPU / hardware fault | BadDeviceFailure |
Degraded |
| Socket / timeout | BadCommunicationError |
Degraded |
Testing
- Unit tests —
tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.S7.Tests/cover the address parser, the reinterpret/box conversions, and the driver lifecycle. - Integration fixture — a Docker S7 simulator on the shared docker host; see S7-Test-Fixture.md for the coverage map and endpoint.
- CLI — Driver.S7.Cli.md documents the standalone read/write/probe CLI for manual checks against a real or simulated CPU.
Further reading
docs/v2/driver-specs.md §5— full per-field spec, DriverConfig JSON shape, and operational stability notes- Driver.S7.Cli.md — standalone S7 driver CLI
- S7-Test-Fixture.md — simulator + test-harness map