Siemens S7
The S7 module supports Siemens S7 TCP, also known as ISO-on-TCP with Read Var / Write Var. It covers DB, I, Q, and M areas for Bool, Byte, Word, DWord, Int, DInt, and Real, plus point acquisition, write-back, and virtual PLC simulation.
Install
dotnet add package Zeus.Communications
dotnet add package Zeus.Protocols.S7
Connect to a Real PLC
S7 TCP commonly uses port 102.
await using var app = ZeusHost.Create(builder =>
{
builder.AddTcpClient("plc-link", "192.168.1.10", 102);
builder.AddSiemensS7("plc", "plc-link", new S7Options
{
Rack = 0,
Slot = 1
});
});
await app.StartAsync();
var plc = app.Devices.Get<S7Device>("plc");
var temperature = await plc.ReadRealAsync(S7Area.DataBlock, byteOffset: 0, dbNumber: 1);
await plc.WriteIntAsync(S7Area.DataBlock, byteOffset: 4, value: 120, dbNumber: 1);
var running = await plc.ReadBoolAsync(S7Area.Merkers, byteOffset: 10, bitOffset: 0);
S7-300/400 often use slot 2; S7-1200/1500 projects often use slot 1. Set rack/slot explicitly when the default does not match the PLC.
Common Types
var dbBytes = await plc.ReadDataBlockBytesAsync(dbNumber: 1, byteOffset: 0, length: 8);
await plc.WriteDataBlockBytesAsync(dbNumber: 1, byteOffset: 20, values: [1, 2, 3, 4]);
var word = await plc.ReadWordAsync(S7Area.DataBlock, 0, dbNumber: 1);
var dint = await plc.ReadDIntAsync(S7Area.DataBlock, 4, dbNumber: 1);
var real = await plc.ReadRealAsync(S7Area.DataBlock, 8, dbNumber: 1);
var bit = await plc.ReadBoolAsync(S7Area.DataBlock, 12, 3, dbNumber: 1);
await plc.WriteWordAsync(S7Area.Merkers, 0, 1234);
await plc.WriteBoolAsync(S7Area.Outputs, 2, 1, true);
S7 multi-byte values use PLC-style big-endian encoding. Bool uses byte offset plus bit offset, so DB1.DBX12.3 maps to area: db, db: 1, address: 12, and bit: 3.
Points and Write-Back
await using var app = ZeusHost.Create(builder =>
{
builder.AddTcpClient("plc-link", "192.168.1.10", 102);
builder.AddSiemensS7("plc", "plc-link", points: map => map
.DbReal("temperature", dbNumber: 1, byteOffset: 0)
.DbInt("setpoint", dbNumber: 1, byteOffset: 4, scale: 0.1).Writable("setpoint")
.MarkerBool("running", byteOffset: 10, bitOffset: 0).Writable("running"));
});
await app.StartAsync();
var temperature = app.Points.Get<float>("temperature");
await app.Points.WriteAsync("setpoint", 12.3);
await app.Points.WriteAsync("running", true);
scale is for integer engineering values. Writing 12.3 with scale: 0.1 writes raw 123 to DB1.DBW4.
Virtual PLC
var memory = new S7SlaveMemory();
await using var app = ZeusHost.Create(builder =>
{
builder.AddVirtualChannel("plc-link", new S7SlaveResponder(memory));
builder.AddSiemensS7("plc", "plc-link");
});
Run sample: samples/Zeus.Samples.Console.S7.
JSON Configuration
{
"channels": [
{ "name": "plc-link", "type": "tcp", "host": "192.168.1.10", "port": 102 }
],
"devices": [
{
"name": "plc",
"channel": "plc-link",
"type": "siemens-s7",
"rack": 0,
"slot": 1,
"points": [
{ "name": "temperature", "area": "db", "db": 1, "address": 0, "dataType": "real" },
{ "name": "setpoint", "area": "db", "db": 1, "address": 4, "dataType": "int", "scale": 0.1, "writable": true },
{ "name": "running", "area": "m", "address": 10, "bit": 0, "dataType": "bool", "writable": true }
]
}
]
}
Virtual channel:
{ "name": "plc-link", "type": "virtual", "responder": "s7" }
Common Issues
| Symptom | Common cause | Fix |
|---|---|---|
| Timeout | IP, port 102, rack/slot, or PLC access setting is wrong | Verify with S7SlaveResponder, then check PLC settings |
| DB value is wrong | Offset, type, or optimized access mismatch | Confirm absolute DB addresses in TIA Portal |
| Bool point is wrong | Bit offset missing or wrong | address is byte offset; bit is 0-7 |
| Input-area write fails | I area is read-only input image | Write Q, M, or DB areas |
Next: Mitsubishi MC or Modbus.