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",
"options": {
"host": "192.168.1.10",
"port": 102
}
}
],
"devices": [
{
"name": "plc",
"channel": "plc-link",
"type": "siemens-s7",
"options": {
"rack": 0,
"slot": 1
},
"points": [
{
"name": "temperature",
"options": {
"area": "db",
"db": 1,
"address": 0,
"dataType": "real"
}
},
{
"name": "setpoint",
"options": {
"area": "db",
"db": 1,
"address": 4,
"dataType": "int",
"scale": 0.1,
"writable": true
}
},
{
"name": "running",
"options": {
"area": "m",
"address": 10,
"bit": 0,
"dataType": "bool",
"writable": true
}
}
]
}
]
}
Virtual channel:
{
"name": "plc-link",
"type": "virtual",
"options": {
"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.